mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
docs: fix image spacing. fix phrasing and various grammar issues (#1333)
This commit is contained in:
parent
d60f0cca7a
commit
94dec53d9a
@ -15,8 +15,9 @@ differences and similarities between each.
|
||||
|
||||
### State Model
|
||||
|
||||
There are no big differences between Zustand and Redux. Both are based on
|
||||
immutable state model. Also, Redux needs to wrap your app in context providers.
|
||||
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'
|
||||
@ -106,8 +107,8 @@ const countSlice = createSlice({
|
||||
initialState: { value: 0 },
|
||||
reducers: {
|
||||
incremented: (state, qty: number) => {
|
||||
// Redux Toolkit does not mutate the state, it use Immer library behind
|
||||
// scenes allow us to have something called "draft state".
|
||||
// Redux Toolkit does not mutate the state, it uses the Immer library
|
||||
// behind scenes, allowing us to have something called "draft state".
|
||||
state.value += qty
|
||||
},
|
||||
decremented: (state, qty: number) => {
|
||||
@ -125,6 +126,8 @@ 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**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
|
||||
@ -151,6 +154,8 @@ const Component = () => {
|
||||
}
|
||||
```
|
||||
|
||||
**Redux**
|
||||
|
||||
```ts
|
||||
import { createStore } from 'redux'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
@ -194,8 +199,8 @@ const countSlice = createSlice({
|
||||
initialState: { value: 0 },
|
||||
reducers: {
|
||||
incremented: (state, qty: number) => {
|
||||
// Redux Toolkit does not mutate the state, it use Immer library behind
|
||||
// scenes allow us to have something called "draft state".
|
||||
// Redux Toolkit does not mutate the state, it uses the Immer library
|
||||
// behind scenes, allowing us to have something called "draft state".
|
||||
state.value += qty
|
||||
},
|
||||
decremented: (state, qty: number) => {
|
||||
@ -222,8 +227,11 @@ const Component = () => {
|
||||
|
||||
### State Model
|
||||
|
||||
There is a major difference between Zustand and Valtio. 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**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
@ -237,6 +245,8 @@ const store = create<State>(() => ({ obj: { count: 0 } }))
|
||||
store.setState((prev) => ({ obj: { count: prev.obj.count + 1 } })
|
||||
```
|
||||
|
||||
**Valtio**
|
||||
|
||||
```ts
|
||||
import { proxy } from 'valtio'
|
||||
|
||||
@ -248,9 +258,11 @@ 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
|
||||
optimizations through property access. However, with Zustand, it is recommended
|
||||
that you manually apply render optimizations by using selectors.
|
||||
|
||||
**Zustand**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
|
||||
@ -268,6 +280,8 @@ const Component = () => {
|
||||
}
|
||||
```
|
||||
|
||||
**Valtio**
|
||||
|
||||
```ts
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
|
||||
@ -285,11 +299,12 @@ const Component = () => {
|
||||
|
||||
### State Model
|
||||
|
||||
There are two major differences between Zustand and Jotai. The first one is
|
||||
Zustand is a single store, while Jotai consists of primitive atoms and allows
|
||||
composing them together. The last one is Zustand store is global in memory, but
|
||||
Jotai atoms are not (are definitions that do not hold values) and that's why
|
||||
you can not use it outside React.
|
||||
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 when access outside of React is required.
|
||||
|
||||
**Zustand**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
@ -311,6 +326,8 @@ const useCountStore = create<State & Actions>((set) => ({
|
||||
}))
|
||||
```
|
||||
|
||||
**Jotai**
|
||||
|
||||
```ts
|
||||
import { atom } from 'jotai'
|
||||
|
||||
@ -319,9 +336,11 @@ const countAtom = atom<number>(0)
|
||||
|
||||
### Render Optimization
|
||||
|
||||
The other difference between Zustand and Jotai is: Jotai makes 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**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
@ -349,6 +368,8 @@ const Component = () => {
|
||||
}
|
||||
```
|
||||
|
||||
**Jotai**
|
||||
|
||||
```ts
|
||||
import { atom, useAtom } from 'jotai'
|
||||
|
||||
@ -364,9 +385,11 @@ const Component = () => {
|
||||
|
||||
### State Model
|
||||
|
||||
The major difference is the same as Zustand and Recoil is: Recoil depends on
|
||||
atom string keys instead of atom object referential identities. Also, Recoil
|
||||
needs to wrap your app in a context provider.
|
||||
The difference between Zustand and Recoil is similar to that between Zustand and
|
||||
Recoil. Recoil depends on atom string keys instead of atom object referential
|
||||
identities, additionally, Recoil needs to wrap your app in a context provider.
|
||||
|
||||
**Zustand**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
@ -386,6 +409,8 @@ const useCountStore = create<State & Actions>((set) => ({
|
||||
}))
|
||||
```
|
||||
|
||||
**Recoil**
|
||||
|
||||
```ts
|
||||
import { atom } from 'recoil'
|
||||
|
||||
@ -397,10 +422,12 @@ const count = atom({
|
||||
|
||||
### Render Optimization
|
||||
|
||||
The other difference between Zustand and Recoil is: Recoil makes render
|
||||
optimizations through atom dependency. However, with Zustand it is recommended that you
|
||||
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**
|
||||
|
||||
```ts
|
||||
import create from 'zustand'
|
||||
|
||||
@ -425,6 +452,8 @@ const Component = () => {
|
||||
}
|
||||
```
|
||||
|
||||
**Recoil**
|
||||
|
||||
```ts
|
||||
import { atom, useRecoilState } from 'recoil'
|
||||
|
||||
|
||||
@ -4,4 +4,6 @@ description:
|
||||
nav: 1
|
||||
---
|
||||
|
||||
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
|
||||
<Hint>
|
||||
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
|
||||
</Hint>
|
||||
|
||||
@ -4,20 +4,23 @@ description: How to use Zustand
|
||||
nav: 0
|
||||
---
|
||||
|
||||
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
|
||||
<Hint>
|
||||
⚠️ This doc is still under construction.
|
||||
https://github.com/pmndrs/zustand/discussions/1033
|
||||
</Hint>
|
||||
|
||||
<p align="center">
|
||||
<img width="600" src="https://github.com/pmndrs/zustand/raw/main/bear.jpg" />
|
||||
</p>
|
||||
<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. Has a comfy api based
|
||||
on hooks, isn't boilerplatey or opinionated, but still just enough to be explicit
|
||||
and flux-like.
|
||||
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 quite the 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.
|
||||
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
|
||||
|
||||
```bash
|
||||
npm install zustand
|
||||
```
|
||||
@ -38,7 +41,7 @@ const useStore = create((set) => ({
|
||||
|
||||
## Then bind your components, and that's it!
|
||||
|
||||
Use the hook anywhere, no providers needed. Select your state and the component will re-render on changes.
|
||||
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() {
|
||||
|
||||
@ -48,7 +48,7 @@ const useCountStore = create((set) => ({
|
||||
```
|
||||
|
||||
For complex use cases, consider using some libraries that help with immutable updates.
|
||||
You can refer to [Updating nested state object values](./updating-nested-state-object-values.md).
|
||||
You can refer to [Updating nested state object values](./updating-state.md#deeply-nested-object).
|
||||
|
||||
## Replace flag
|
||||
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Updating nested state object values
|
||||
nav: 3
|
||||
---
|
||||
|
||||
## Deeply nested object
|
||||
|
||||
If you have a deep state object like this:
|
||||
|
||||
```ts
|
||||
type State = {
|
||||
deep: {
|
||||
nested: {
|
||||
obj: { count: number }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It requires some effort to update the count value immutably.
|
||||
|
||||
## Normal approach
|
||||
|
||||
The normal approach is to copy state object with the spread operator `...` like so:
|
||||
|
||||
```ts
|
||||
normalInc: () =>
|
||||
set((state) => ({
|
||||
deep: {
|
||||
...state.deep,
|
||||
nested: {
|
||||
...state.deep.nested,
|
||||
obj: {
|
||||
...state.deep.nested.obj,
|
||||
count: state.deep.nested.obj.count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
```
|
||||
|
||||
This is very long!
|
||||
|
||||
## With immer
|
||||
|
||||
Many people use [immer](https://github.com/immerjs/immer) to update nested values. You can use immer to shorten your state updates for deeply nested object like this:
|
||||
|
||||
```ts
|
||||
immerInc: () =>
|
||||
set(produce((state: State) => { ++state.deep.nested.obj.count })),
|
||||
```
|
||||
|
||||
What a reduction!. [Please take note of the gotchas listed here](../integrations/updating-draft-states.md).
|
||||
|
||||
## With optics-ts
|
||||
|
||||
There is another option with [optics-ts](https://github.com/akheron/optics-ts/):
|
||||
|
||||
```ts
|
||||
opticsInc: () =>
|
||||
set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),
|
||||
```
|
||||
|
||||
Unlike immer, optics-ts doesn't use proxies or mutation syntax.
|
||||
|
||||
## With ramda
|
||||
|
||||
You can also use [ramda](https://ramdajs.com/):
|
||||
|
||||
```ts
|
||||
ramdaInc: () =>
|
||||
set(R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)),
|
||||
```
|
||||
|
||||
Both ramda and optics-ts also work with types.
|
||||
|
||||
## CodeSandbox Demo
|
||||
|
||||
https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx
|
||||
141
docs/guides/updating-state.md
Normal file
141
docs/guides/updating-state.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
title: Updating state
|
||||
nav: 3
|
||||
---
|
||||
|
||||
## Flat updates
|
||||
|
||||
Updating state with Zustand is simple! Call the provided `set` function with
|
||||
the new state, and it will be shallowly merged with the existing state in the
|
||||
store. **Note** See next section for nested state.
|
||||
|
||||
```tsx
|
||||
type State = {
|
||||
firstName: string
|
||||
lastName: string
|
||||
}
|
||||
|
||||
type Action = {
|
||||
updateFirstName: (firstName: State['firstName']) => void
|
||||
updateLastName: (lastName: State['lastName']) => void
|
||||
}
|
||||
|
||||
// Create your store, which includes both state and (optionally) actions
|
||||
const useStore = create<State & Action>((set) => ({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
|
||||
updateLastName: (lastName) => set(() => ({ lastName: lastName })),
|
||||
}))
|
||||
|
||||
// In consuming app
|
||||
function App() {
|
||||
// "select" the needed state and actions, in this case, the firstName value
|
||||
// and the action updateFirstName
|
||||
const [firstName, updateFirstName] = useStore(
|
||||
(state) => [state.firstName, state.updateFirstName],
|
||||
shallow
|
||||
)
|
||||
|
||||
return (
|
||||
<main>
|
||||
<label>
|
||||
First name
|
||||
<input
|
||||
// Update the "firstName" state
|
||||
onChange={(e) => updateFirstName(e.currentTarget.value)}
|
||||
value={firstName}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<p>
|
||||
Hello, <strong>{firstName}!</strong>
|
||||
</p>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Deeply nested object
|
||||
|
||||
If you have a deep state object like this:
|
||||
|
||||
```ts
|
||||
type State = {
|
||||
deep: {
|
||||
nested: {
|
||||
obj: { count: number }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Updating nested state requires some effort to ensure the process is completed
|
||||
immutably.
|
||||
|
||||
### Normal approach
|
||||
|
||||
Similar to React or Redux, the normal approach is to copy each level of the
|
||||
state object. This is done with the spread operator `...`, and by manually
|
||||
merging that in with the new state values. Like so:
|
||||
|
||||
```ts
|
||||
normalInc: () =>
|
||||
set((state) => ({
|
||||
deep: {
|
||||
...state.deep,
|
||||
nested: {
|
||||
...state.deep.nested,
|
||||
obj: {
|
||||
...state.deep.nested.obj,
|
||||
count: state.deep.nested.obj.count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
})),
|
||||
```
|
||||
|
||||
This is very long! Let's explore some alternatives that will make your life
|
||||
easier.
|
||||
|
||||
### With immer
|
||||
|
||||
Many people use [immer](https://github.com/immerjs/immer) to update nested
|
||||
values. Immer can be used anytime you need to update nested state such as in
|
||||
React, Redux and of course, Zustand!
|
||||
|
||||
You can use immer to shorten your state updates for deeply nested object. Let's
|
||||
take a look at an example:
|
||||
|
||||
```ts
|
||||
immerInc: () =>
|
||||
set(produce((state: State) => { ++state.deep.nested.obj.count })),
|
||||
```
|
||||
|
||||
What a reduction!. [Please take note of the gotchas listed here](../integrations/updating-draft-states.md).
|
||||
|
||||
### With optics-ts
|
||||
|
||||
There is another option with [optics-ts](https://github.com/akheron/optics-ts/):
|
||||
|
||||
```ts
|
||||
opticsInc: () =>
|
||||
set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),
|
||||
```
|
||||
|
||||
Unlike immer, optics-ts doesn't use proxies or mutation syntax.
|
||||
|
||||
### With Ramda
|
||||
|
||||
You can also use [Ramda](https://ramdajs.com/):
|
||||
|
||||
```ts
|
||||
ramdaInc: () =>
|
||||
set(R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)),
|
||||
```
|
||||
|
||||
Both ramda and optics-ts also work with types.
|
||||
|
||||
### CodeSandbox Demo
|
||||
|
||||
https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx
|
||||
@ -277,7 +277,7 @@ const clearForest = useLushStore((state) => state.clearForest)
|
||||
clearForest()
|
||||
```
|
||||
|
||||
[Alternatively, there are some other solutions.](./docs/guides/updating-nested-state-object-values.md)
|
||||
[Alternatively, there are some other solutions.](./docs/guides/updating-state.md#with-immer)
|
||||
|
||||
## Middleware
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user