Remove warnings and clean up getting started docs (#1559)

* docs: remove now superfluous warning messages

Co-authored-by: Chris K <55621012+chrisk-7777@users.noreply.github.com>

* docs: clean up getting started doc group

* docs: remove unnecessary comma from comparison page

* docs: reformat recipies (1/2)

* docs: reformat recipies (2/2)

Co-authored-by: Chris K <55621012+chrisk-7777@users.noreply.github.com>
This commit is contained in:
Blazej Sewera 2023-01-20 01:06:17 +01:00 committed by GitHub
parent 48c962ce67
commit 2f2fee4803
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 196 additions and 138 deletions

View File

@ -1,23 +1,25 @@
---
title: Comparison
description:
nav: 2
description: How Zustand stacks up against similar libraries
nav: 1
---
Zustand is one of many state management libraries for React. On this page we
will discuss Zustand in comparison to some of these libraries, including Redux,
Valtio, Jotai, and Recoil.
Zustand is one of many state management libraries for React.
On this page we will discuss Zustand
in comparison to some of these libraries,
including Redux, Valtio, Jotai, and Recoil.
Each library has its own strengths and weaknesses, and we will compare key
differences and similarities between each.
Each library has its own strengths and weaknesses,
and we will compare key differences and similarities between each.
## Redux
### State Model
Conceptually, Zustand and Redux are quite similar, both are based on an
immutable state model. However, Redux, requires your app to be wrapped in
context providers; Zustand does not.
Conceptually, Zustand and Redux are quite similar,
both are based on an immutable state model.
However, Redux requires your app to be wrapped
in context providers; Zustand does not.
```ts
import { create } from 'zustand'
@ -122,9 +124,10 @@ const countStore = configureStore({ reducer: countSlice.reducer })
### Render Optimization
When it comes to render optimizations within your app, there are no major
differences in approach between Zustand and Redux. In both libraries it is
recommended that you manually apply render optimizations by using selectors.
When it comes to render optimizations within your app,
there are no major differences in approach between Zustand and Redux.
In both libraries it is recommended
that you manually apply render optimizations by using selectors.
**Zustand**
@ -227,9 +230,10 @@ const Component = () => {
### State Model
Zustand and Valtio approach state management in a fundamentally different way.
Zustand is based on the **immutable** state model, while Valtio is based on the
**mutable** state model.
Zustand and Valtio approach state management
in a fundamentally different way.
Zustand is based on the **immutable** state model,
while Valtio is based on the **mutable** state model.
**Zustand**
@ -257,9 +261,10 @@ state.obj.count += 1
### Render Optimization
The other difference between Zustand and Valtio is Valtio makes render
optimizations through property access. However, with Zustand, it is recommended
that you manually apply render optimizations by using selectors.
The other difference between Zustand and Valtio
is Valtio makes render optimizations through property access.
However, with Zustand, it is recommended that
you manually apply render optimizations by using selectors.
**Zustand**
@ -299,10 +304,12 @@ const Component = () => {
### State Model
There are two major differences between Zustand and Jotai. Firstly, Zustand is a
single store, while Jotai consists of primitive atoms that can be composed
together. Secondly, a Zustand store is an external store, making it more
suitable when access outside of React is required.
There are two major differences between Zustand and Jotai.
Firstly, Zustand is a single store,
while Jotai consists of primitive atoms
that can be composed together.
Secondly, a Zustand store is an external store,
making it more suitable when access outside of React is required.
**Zustand**
@ -336,9 +343,9 @@ const countAtom = atom<number>(0)
### Render Optimization
Jotai achieves render optimizations through atom dependency. However, with
Zustand it is recommended that you manually apply render optimizations by using
selectors.
Jotai achieves render optimizations through atom dependency.
However, with Zustand it is recommended that
you manually apply render optimizations by using selectors.
**Zustand**
@ -385,9 +392,11 @@ const Component = () => {
### State Model
The difference between Zustand and Recoil is similar to that between Zustand and
Jotai. Recoil depends on atom string keys instead of atom object referential
identities, additionally, Recoil needs to wrap your app in a context provider.
The difference between Zustand and Recoil
is similar to that between Zustand and Jotai.
Recoil depends on atom string keys
instead of atom object referential identities.
Additionally, Recoil needs to wrap your app in a context provider.
**Zustand**
@ -422,9 +431,10 @@ const count = atom({
### Render Optimization
Similar to previous optimization comparisons, Recoil makes render optimizations
through atom dependency. Whereas, with Zustand, it is recommended that you
manually apply render optimizations by using selectors.
Similar to previous optimization comparisons,
Recoil makes render optimizations through atom dependency.
Whereas with Zustand, it is recommended that
you manually apply render optimizations by using selectors.
**Zustand**

View File

@ -1,9 +0,0 @@
---
title: Concepts
description:
nav: 1
---
<Hint>
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
</Hint>

View File

@ -0,0 +1,73 @@
---
title: Introduction
description: How to use Zustand
nav: 0
---
<div class="flex justify-center mb-4">
<img src="https://github.com/pmndrs/zustand/raw/main/bear.jpg" />
</div>
A small, fast, and scalable bearbones state management solution.
Zustand has a comfy API based on hooks.
It isn't boilerplatey or opinionated,
but has enough convention to be explicit and flux-like.
Don't disregard it because it's cute, it has claws!
Lots of time was spent to deal with common pitfalls,
like the dreaded [zombie child problem],
[React concurrency], and [context loss]
between mixed renderers.
It may be the one state manager in the React space that gets all of these right.
You can try a live demo [here](https://codesandbox.io/s/dazzling-moon-itop4).
[zombie child problem]: https://react-redux.js.org/api/hooks#stale-props-and-zombie-children
[React concurrency]: https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md
[context loss]: https://github.com/facebook/react/issues/13332
## Installation
Zustand is available as a package on NPM for use:
```bash
# NPM
npm install zustand
# Yarn
yarn add zustand
```
## First create a store
Your store is a hook!
You can put anything in it: primitives, objects, functions.
The `set` function _merges_ state.
```js
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
```
## Then bind your components, and that's it!
You can use the hook anywhere, without the need of providers.
Select your state and the consuming component
will re-render when that state changes.
```jsx
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
```

View File

@ -1,62 +0,0 @@
---
title: Introduction
description: How to use Zustand
nav: 0
---
<Hint>
⚠️ This doc is still under construction.
https://github.com/pmndrs/zustand/discussions/1033
</Hint>
<div class="flex justify-center mb-4">
<img src="https://github.com/pmndrs/zustand/raw/main/bear.jpg" />
</div>
A small, fast and scalable bearbones state-management solution. Zustand has a comfy api based on hooks. It isn't boilerplatey or opinionated, but still has enough to be explicit and flux-like.
Don't disregard it because it's cute, it has claws! Lots of time was spent to deal with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [React concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.
You can try a live demo [here](https://codesandbox.io/s/dazzling-moon-itop4).
## Installation
Zustand is available as a package on NPM for use:
```bash
# NPM
npm install zustand
# Yarn
yarn add zustand
```
## First create a store
Your store is a hook! You can put anything in it: primitives, objects, functions. The `set` function _merges_ state.
```jsx
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
```
## Then bind your components, and that's it!
You can use the hook anywhere, without the need of providers. Select your state and the consuming component will re-render when that state changes.
```jsx
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here ...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
```

View File

@ -4,11 +4,10 @@ description: How to do all you need with Zustand
nav: 15
---
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
## Fetching everything
You can, but bear in mind that it will cause the component to update on every state change!
You can, but bear in mind that it will cause
the component to update on every state change!
```jsx
const state = useStore()
@ -16,14 +15,16 @@ const state = useStore()
## Selecting multiple state slices
It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.
It detects changes with strict-equality (`old === new`) by default.
This is efficient for atomic state picks.
```jsx
const nuts = useStore((state) => state.nuts)
const honey = useStore((state) => state.honey)
```
For more control over re-rendering, you may provide an alternative equality function on the second argument.
For more control over re-rendering,
you may provide an alternative equality function on the second argument.
```jsx
const treats = useStore(
@ -32,7 +33,11 @@ const treats = useStore(
)
```
For instance, if you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function.
For instance, if you want to construct
a single object with multiple state-picks inside,
similar to Redux's `mapStateToProps`,
you can tell Zustand that you want the object
to be diffed shallowly by passing the `shallow` equality function.
```jsx
import { shallow } from 'zustand/shallow'
@ -52,7 +57,8 @@ const treats = useStore((state) => Object.keys(state.treats), shallow)
## Fetching from multiple stores
Since you can create as many stores as you like, forwarding results to succeeding selectors is as natural as it gets.
Since you can create as many stores as you like,
forwarding results to succeeding selectors is as natural as it gets.
```jsx
const currentBear = useCredentialsStore((state) => state.currentBear)
@ -61,24 +67,32 @@ const bear = useBearStore((state) => state.bears[currentBear])
## Memoizing selectors
It is generally recommended to memoize selectors with useCallback. This will prevent unnecessary computations each render. It also allows React to optimize performance in concurrent mode.
It is generally recommended to memoize selectors with `useCallback`.
This will prevent unnecessary computations each render.
It also allows React to optimize performance in concurrent mode.
```jsx
const fruit = useStore(useCallback((state) => state.fruits[id], [id]))
```
If a selector doesn't depend on scope, you can define it outside the render function to obtain a fixed reference without useCallback.
If a selector doesn't depend on scope,
you can define it outside the render function
to obtain a fixed reference without `useCallback`.
```jsx
const selector = state => state.berries
const selector = (state) => state.berries
function Component() {
const berries = useStore(selector)
// ...
}
```
## Overwriting state
The `set` function has a second argument, `false` by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions.
The `set` function has a second argument, `false` by default.
Instead of merging, it will replace the state model.
Be careful not to wipe out parts you rely on, like actions.
```jsx
import omit from 'lodash-es/omit'
@ -93,7 +107,8 @@ const useStore = create((set) => ({
## Async actions
Just call `set` when you're ready, zustand doesn't care if your actions are async or not.
Just call `set` when you're ready,
zustand doesn't care if your actions are async or not.
```jsx
const useStore = create((set) => ({
@ -107,33 +122,40 @@ const useStore = create((set) => ({
## Read from state in actions
`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`.
`set` allows fn-updates `set(state => result)`,
but you still have access to state outside of it through `get`.
```jsx
const useStore = create((set, get) => ({
sound: "grunt",
sound: 'grunt',
action: () => {
const sound = get().sound
// ...
}
})
},
}))
```
## Reading/writing state and reacting to changes outside of components
Sometimes you need to access state in a non-reactive way, or act upon the store. For these cases the resulting hook has utility functions attached to its prototype.
Sometimes you need to access state in a non-reactive way,
or act upon the store.
For these cases the resulting hook
has utility functions attached to its prototype.
If you need to subscribe with selector, subscribeWithSelector middleware will help.
With this middleware subscribe accepts an additional signature:
If you need to subscribe with selector,
`subscribeWithSelector` middleware will help.
With this middleware, subscribe accepts an additional signature:
```jsx
subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe
```
```jsx
import { create } from 'zustand';
import { create } from 'zustand'
import { subscribeWithSelector, shallow } from 'zustand/middleware'
const useStore = create(subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })))
const useStore = create(
subscribeWithSelector(() => ({ paw: true, snout: true, fur: true }))
)
// Getting non-reactive fresh state
const paw = useStore.getState().paw
@ -165,12 +187,16 @@ useStore.destroy()
// You can of course use the hook as you always would
function Component() {
const paw = useStore(state => state.paw)
const paw = useStore((state) => state.paw)
// ...
}
```
## Using zustand without React
Zustands core can be imported and used without the React dependency. The only difference is that the create function does not return a hook, but the api utilities.
Zustand's core can be imported and used without the React dependency.
The only difference is that the create function does not return a hook,
but the API utilities.
```jsx
import { createStore } from 'zustand/vanilla'
@ -188,9 +214,14 @@ import { vanillaStore } from './vanillaStore'
const useStore = create(vanillaStore)
```
## Transient updates (for often occurring state-changes)
## Transient updates (for frequent state changes)
The subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly.
The `subscribe` function allows components to bind
to a state portion without forcing a re-render on changes.
It is best to combine it with `useEffect`
for automatic unsubscribe on unmount.
This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws)
performance impact, when you are allowed to mutate the view directly.
```jsx
const useStore = create(set => ({ scratches: 0, ... }))
@ -203,11 +234,14 @@ function Component() {
scratches => (scratchRef.current = scratches),
state => state.scratches
), [])
// ...
}
```
## Sick of reducers and changing nested state? Use Immer!
Reducing nested structures is tiresome. Have you tried [immer](https://github.com/mweststrate/immer)?
Reducing nested structures is tiresome.
Have you tried [Immer](https://github.com/immerjs/immer)?
```jsx
import produce from 'immer'
@ -326,7 +360,7 @@ export const useStore = create(
)
```
## Can't live without redux-like reducers and action types?
## Can't live without Redux-like reducers and action types?
```jsx
const types = { increase: 'INCREASE', decrease: 'DECREASE' }
@ -349,7 +383,9 @@ const dispatch = useStore((state) => state.dispatch)
dispatch({ type: types.increase, by: 2 })
```
Or, just use our redux-middleware. It wires up your main-reducer, sets initial state, and adds a dispatch function to the state itself and the vanilla api.
Or, just use our `redux` middleware.
It wires up your main reducer, sets initial state,
and adds a dispatch function to the state itself and the vanilla API.
```jsx
import { redux } from 'zustand/middleware'
@ -359,8 +395,13 @@ const useStore = create(redux(reducer, initialState))
## Calling actions outside a React event handler
Because React handles `setState` synchronously if it's called outside an event handler. Updating the state outside an event handler will force react to update the components synchronously, therefore adding the risk of encountering the zombie-child effect.
In order to fix this, the action needs to be wrapped in `unstable_batchedUpdates`
Because React handles `setState` synchronously
if it's called outside an event handler.
Updating the state outside an event handler
will force react to update the components synchronously,
therefore adding the risk of encountering the zombie-child effect.
In order to fix this,
the action needs to be wrapped in `unstable_batchedUpdates`.
```jsx
import { unstable_batchedUpdates } from 'react-dom' // or 'react-native'
@ -377,7 +418,7 @@ const nonReactCallback = () => {
}
```
More details: https://github.com/pmndrs/zustand/issues/302
More details in [this issue](https://github.com/pmndrs/zustand/issues/302).
## Redux devtools
@ -392,8 +433,13 @@ const useStore = create(devtools(redux(reducer, initialState)))
const useStore = create(devtools(store, { enabled: false }))
```
devtools takes the store function as its first argument, optionally you can name the store with a second argument: `devtools(store, "MyStore")`, which will be prefixed to your actions.
devtools will only log actions from each separated store unlike in a typical _combined reducers_ redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163
The `devtools` middleware takes the store function as its first argument.
Optionally, you can name the store with a second argument:
`devtools(store, "MyStore")`, which will be prefixed to your actions.
`devtools` will only log actions from each separated store,
unlike in a typical _combined reducers_ Redux store.
See an approach to combining stores [here](https://github.com/pmndrs/zustand/issues/163).
## TypeScript
@ -409,7 +455,7 @@ const useStore = create<State>((set) => ({
}))
```
You can also use an `interface`:
You can also use an interface:
```tsx
import { State } from 'zustand'
@ -420,7 +466,7 @@ interface BearState extends State {
}
```
Or, use `combine` and let tsc infer types.
Or use `combine` and let `tsc` infer types.
```tsx
import { combine } from 'zustand/middleware'