Comparing version 0.2.0 to 0.2.1
@@ -21,2 +21,2 @@ export declare type State = Record<string, any>; | ||
} | ||
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>) => TState): [UseStore<TState>, StoreApi<TState>]; | ||
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>, api: any) => TState): [UseStore<TState>, StoreApi<TState>]; |
@@ -99,2 +99,3 @@ 'use strict'; | ||
return selector ? subscribe( // Truthy check because it might be possible to set selectorRef to | ||
// undefined and call this subscriber before it resubscribes | ||
function () { | ||
@@ -108,4 +109,3 @@ return selectorRef.current ? selectorRef.current(state) : state; | ||
var state = createState(setState, getState); | ||
return [useStore, { | ||
var api = { | ||
destroy: destroy, | ||
@@ -115,5 +115,7 @@ getState: getState, | ||
subscribe: subscribe | ||
}]; | ||
}; | ||
var state = createState(setState, getState, api); | ||
return [useStore, api]; | ||
} | ||
module.exports = create; |
@@ -21,2 +21,2 @@ export declare type State = Record<string, any>; | ||
} | ||
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>) => TState): [UseStore<TState>, StoreApi<TState>]; | ||
export default function create<TState extends State>(createState: (set: SetState<State>, get: GetState<State>, api: any) => TState): [UseStore<TState>, StoreApi<TState>]; |
@@ -85,2 +85,3 @@ import { useRef, useReducer, useLayoutEffect } from 'react'; | ||
return selector ? subscribe( // Truthy check because it might be possible to set selectorRef to | ||
// undefined and call this subscriber before it resubscribes | ||
() => selectorRef.current ? selectorRef.current(state) : state, dispatch) : subscribe(dispatch); // Only resubscribe to the store when changing selector from function to | ||
@@ -92,4 +93,3 @@ // undefined or undefined to function | ||
let state = createState(setState, getState); | ||
return [useStore, { | ||
let api = { | ||
destroy, | ||
@@ -99,5 +99,7 @@ getState, | ||
subscribe | ||
}]; | ||
}; | ||
let state = createState(setState, getState, api); | ||
return [useStore, api]; | ||
} | ||
export default create; |
{ | ||
"name": "zustand", | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"description": "🐻 Bear necessities for state management in React", | ||
@@ -74,5 +74,2 @@ "main": "dist/cjs/index.js", | ||
"tests/**/*.{js,ts,tsx}" | ||
], | ||
"setupFilesAfterEnv": [ | ||
"<rootDir>/setupTests.js" | ||
] | ||
@@ -86,8 +83,7 @@ }, | ||
"@babel/preset-env": "^7.4.3", | ||
"@testing-library/react": "^8.0.1", | ||
"@types/jest": "^24.0.11", | ||
"@types/react": "^16.8.13", | ||
"enzyme": "^3.9.0", | ||
"enzyme-adapter-react-16": "^1.12.1", | ||
"husky": "^1.3.1", | ||
"jest": "^24.7.1", | ||
"husky": "^2.3.0", | ||
"jest": "^24.8.0", | ||
"lint-staged": "^8.1.5", | ||
@@ -97,10 +93,9 @@ "prettier": "^1.17.0", | ||
"react-dom": "^16.8.6", | ||
"react-testing-library": "^6.1.2", | ||
"rimraf": "^2.6.3", | ||
"rollup": "^1.10.0", | ||
"rollup": "^1.13.1", | ||
"rollup-plugin-babel": "^4.3.2", | ||
"rollup-plugin-node-resolve": "^4.2.3", | ||
"rollup-plugin-size-snapshot": "^0.8.0", | ||
"rollup-plugin-typescript2": "^0.20.1", | ||
"typescript": "^3.4.3" | ||
"rollup-plugin-node-resolve": "^5.0.1", | ||
"rollup-plugin-size-snapshot": "^0.9.0", | ||
"rollup-plugin-typescript2": "^0.21.1", | ||
"typescript": "^3.5.1" | ||
}, | ||
@@ -107,0 +102,0 @@ "peerDependencies": { |
108
readme.md
@@ -9,19 +9,26 @@ <p align="center"> | ||
Small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn't that boilerplatey or opinionated, but still just enough to be explicit and flux-like, not context based (no reliance on providers, breaches reconciler boundaries), and is cross-platform to boot. Make your paws dirty with a small live demo [here](https://codesandbox.io/s/v8pjv251w7). | ||
Small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn't that boilerplatey or opinionated, but still just enough to be explicit and flux-like. Make your paws dirty with a small live demo [here](https://codesandbox.io/s/v8pjv251w7). | ||
#### Create a store (or multiple, up to you...) | ||
#### Why zustand over redux? This lib ... | ||
You could be in global or component scope, manage your store anywhere you want! | ||
1. is simpler and un-opinionated | ||
2. makes hooks the primary means of consuming state | ||
3. isn't dependent on actions, types & dispatch | ||
4. supports [mixed reconcilers](https://github.com/konvajs/react-konva/issues/188) | ||
5. has a solution for rapidpy changing state (look for transient updates) | ||
### How to use it | ||
#### First create a store (or multiple, up to you...) | ||
Your store is a hook! Name it anything you like. Everything inside `create` is your state. There are no rules, you can put anything in it. Actions are not special, you don't need to group them. The `set` function works like Reacts setState, it *merges* state. | ||
```jsx | ||
import create from 'zustand' | ||
// You store is a hook! Name it as you like | ||
const [useStore] = create(set => ({ | ||
// Everything in here is your state | ||
count: 1, | ||
actions: { | ||
// You don't have to nest your actions, but makes it easier to fetch them later on | ||
inc: () => set(state => ({ count: state.count + 1 })), // same semantics as setState | ||
dec: () => set(state => ({ count: state.count - 1 })), // ... it *merges* state | ||
inc: () => set(state => ({ count: state.count + 1 })), | ||
dec: () => set(state => ({ count: state.count - 1 })), | ||
}, | ||
@@ -31,9 +38,8 @@ })) | ||
#### Bind components | ||
#### Then bind components with the resulting hook, that's it! | ||
Look Ma, no providers! | ||
Use the hook anywhere, you are not tied to providers and sub-trees. Once you have selected state your component will re-render whenever your selection changes in the store. | ||
```jsx | ||
function Counter() { | ||
// Will only re-render the component when "count" changes | ||
const count = useStore(state => state.count) | ||
@@ -44,3 +50,2 @@ return <h1>{count}</h1> | ||
function Controls() { | ||
// "actions" isn't special, in this case it makes fetching updaters easier | ||
const actions = useStore(state => state.actions) | ||
@@ -68,3 +73,3 @@ return ( | ||
It's just like mapStateToProps in Redux. zustand will run a small shallow equal over the object you return. Of course, it won't cause re-renders if these properties aren't changed in the state model. | ||
Just like with Reduxes mapStateToProps, useStore can select state, either atomically or by returning an object. It will run a small shallow-equal test over the results you return and update the component on changes only. | ||
@@ -75,3 +80,3 @@ ```jsx | ||
Or, if you prefer, atomic selects do the same ... | ||
Atomic selects do the same ... | ||
@@ -85,3 +90,3 @@ ```jsx | ||
Since you can create as many stores as you like, forwarding a result into another selector is straight forward. | ||
Since you can create as many stores as you like, forwarding results to succeeding selectors is as natural as it gets. | ||
@@ -95,3 +100,3 @@ ```jsx | ||
Flux stores usually call the selector on every render-pass. Most of the time this isn't much of a problem, but when your selectors are computationally expensive, or when you know the component renders a lot (for instance react-motion calling it 60 times per second for animation purposes) you may want to optimize it. | ||
Say you select a piece of state ... | ||
@@ -102,3 +107,3 @@ ```js | ||
In this case the selector `state => state.foo[props.id]` will run on every state change, as well as every time the component renders. This isn't expensive at all, but let's optimize it for arguments sake. | ||
Your selector (`state => state.foo[props.id]`) will run on every state change, as well as every time the component renders. It isn't that expensive in this case, but let's optimize it for arguments sake. | ||
@@ -112,3 +117,3 @@ You can either pass a static reference: | ||
Or an optional dependencies array to let Zustand know when the selector updates: | ||
Or an optional dependencies array to let zustand know when the selector needs to update: | ||
@@ -119,3 +124,3 @@ ```js | ||
From there on your selector will only run when either state changes, or the selector itself. | ||
From now on your selector is memoized and will only run when either the state changes, or the selector itself. | ||
@@ -128,9 +133,6 @@ ## Async actions | ||
const [useStore] = create(set => ({ | ||
result: '', | ||
json: {}, | ||
fetch: async url => { | ||
const response = await fetch(url) | ||
const json = await response.json() | ||
set({ result: json }) | ||
}, | ||
})) | ||
set({ json: await response.json() }) | ||
``` | ||
@@ -140,3 +142,3 @@ | ||
The `set` function already allows functional update `set(state => result)` but should there be cases where you need to access outside of it you have an optional `get`, too. | ||
`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`. | ||
@@ -148,5 +150,2 @@ ```jsx | ||
const text = get().text | ||
... | ||
} | ||
})) | ||
``` | ||
@@ -156,3 +155,3 @@ | ||
Having to build nested structures bearhanded is one of the more tiresome aspects of reducing state. Have you tried [immer](https://github.com/mweststrate/immer)? It is a tiny package that allows you to work with immutable state in a more convenient way. You can easily extend your store with it. | ||
Reducing nested structures is tiresome. Have you tried [immer](https://github.com/mweststrate/immer)? | ||
@@ -164,16 +163,7 @@ ```jsx | ||
set: fn => set(produce(fn)), | ||
nested: { | ||
structure: { | ||
constains: { | ||
a: "value" | ||
} | ||
} | ||
}, | ||
nested: { structure: { constains: { a: "value" } } }, | ||
})) | ||
const set = useStore(state => state.set) | ||
set(draft => { | ||
draft.nested.structure.contains.a.value = false | ||
draft.nested.structure.contains.anotherValue = true | ||
}) | ||
set(state => void state.nested.structure.contains = null) | ||
``` | ||
@@ -184,6 +174,3 @@ | ||
```jsx | ||
const types = { | ||
increase: "INCREASE", | ||
decrease: "DECREASE" | ||
} | ||
const types = { increase: "INCREASE", decrease: "DECREASE" } | ||
@@ -211,14 +198,15 @@ const reducer = (state, { type, by = 1 }) => { | ||
```jsx | ||
const [, api] = create({ n: 0 }) | ||
const [, api] = create({ a: 1, b: 2, c: 3 }) | ||
// Getting fresh state | ||
const num = api.getState().n | ||
// Listening to changes | ||
const unsub = api.subscribe(state => console.log(state.n)) | ||
// And with a selector | ||
const unsub2 = api.subscribe(state => state.n, n => console.log(n)) | ||
// Listening to all changes, fires on every dispatch | ||
const unsub1 = api.subscribe(state => console.log("state changed", state)) | ||
// Listening to selected changes | ||
const unsub2 = api.subscribe(state => state.a, a => console.log("a changed", a)) | ||
// Updating state, will trigger listeners | ||
api.setState({ n: 1 }) | ||
// Unsubscribing listener | ||
unsub() | ||
api.setState({ a: 1 }) | ||
// Unsubscribe listeners | ||
unsub1() | ||
unsub2() | ||
// Destroying the store (removing all listeners) | ||
@@ -228,4 +216,20 @@ api.destroy() | ||
## Transient updates (for often occuring state-changes) | ||
The api signature of subscribe([selector,] callback):unsub allows you to easily bind a component to a store without forcing it to re-render on state changes, you will be notified in a callback instead. Best combine it with useEffect. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance difference when you are allowed to mutate the view directly. | ||
```jsx | ||
const [useStore, api] = create(set => ({ [0]: [-10, 0], [1]: [10, 5], ... })) | ||
function Component({ id }) { | ||
// Fetch initial state | ||
const xy = useRef(api.getState()[id]) | ||
// Connect to the store on mount, disconnect on unmount, catch state-changes in a callback | ||
useEffect(() => api.subscribe(state => state[id], coords => (xy.current = coords)), [id]) | ||
``` | ||
## Middleware | ||
You can functionally compose your store any way you like. | ||
```jsx | ||
@@ -232,0 +236,0 @@ const logger = fn => (set, get) => fn(args => { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
19444
21
215
229
0