Add usage with context to docs (#1291)

* docs: context guide proposal

* fix(docs): typo & omit comment

* fix(docs): remove horizontal rules

* fix(docs): add wiki link to DI

* fix(docs): format with prettier

* fix(docs): cases that model => where... is needed

* fix(docs): use sentence case

* fix(docs): omit quote block

* fix(docs): into "a" custom hook

* fix(docs): format with prettier

* fix(docs): sort broken nav indexes

* Update docs/guides/initialize-state-with-props.md

Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>

* fix(docs): reintroduce React.createContext example

* scripts: escape quotes

* styles: run prettier

* fix(docs): omit 'React' from imports

* styles: run prettier

* styles: run prettier

Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
This commit is contained in:
Jacob Bergholtz 2022-09-17 19:01:06 -05:00 committed by GitHub
parent eea3944499
commit 4ed81bc11b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 163 additions and 20 deletions

View File

@ -1,6 +1,6 @@
---
title: Auto Generating Selectors
nav: 7
nav: 6
---
We recommend using selectors when using either the properties or actions from the store. You can access values from the store like so:

View File

@ -1,6 +1,6 @@
---
title: Connect to state with URL hash
nav: 15
nav: 12
---
## State is connected with URL hash

View File

@ -1,6 +1,6 @@
---
title: Calling actions outside a React event handler in pre React 18
nav: 11
nav: 10
---
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, there is a risk of encountering the zombie-child effect.

View File

@ -1,6 +1,6 @@
---
title: Flux inspired practice
nav: 6
nav: 5
---
Although zustand is an unopinionated library, here are some patterns we recommend:

View File

@ -1,6 +1,6 @@
---
title: How to reset state
nav: 18
nav: 13
---
The following pattern can be used to reset the state to its initial value.

View File

@ -1,6 +1,6 @@
---
title: Immutable state and merging
nav: 5
nav: 4
---
Like with React's `useState`, we need to update state immutably.

View File

@ -0,0 +1,143 @@
---
title: Initialize state with props
nav: 14
---
In cases where [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) is needed, such as when a store should be initialized with props from a component, the recommended approach is to use a vanilla store with React.context.
## Store creator with `createStore`
```ts
import { createStore } from 'zustand'
interface BearProps {
bears: number
}
interface BearState extends BearProps {
addBear: () => void
}
type BearStore = ReturnType<typeof createBearStore>
const createBearStore = (initProps?: Partial<BearProps>) => {
const DEFAULT_PROPS: BearProps = {
bears: 0,
}
return createStore<BearState>()((set) => ({
...DEFAULT_PROPS,
...initProps,
addBear: () => set((state) => ({ bears: ++state.bears })),
}))
}
```
## Creating a context with `React.createContext`
```ts
import { createContext } from 'react'
export const BearContext = createContext<BearStore | null>(null)
```
## Basic component usage
```tsx
// Provider implementation
import { useRef } from 'react'
function App() {
const store = useRef(createBearStore()).current
return (
<BearContext.Provider value={store}>
<BasicConsumer />
</BearContext.Provider>
)
}
```
```tsx
// Consumer component
import { useContext } from 'react'
import { useStore } from 'zustand'
function BasicConsumer() {
const store = useContext(BearContext)
if (!store) throw new Error('Missing BearContext.Provider in the tree')
const bears = useStore(store, (s) => s.bears)
const addBear = useStore(store, (s) => s.addBear)
return (
<>
<div>{bears} Bears.</div>
<button onClick={addBear}>Add bear</button>
</>
)
}
```
## Common patterns
### Wrapping the context provider
```tsx
// Provider wrapper
import { useRef } from 'react'
type BearProviderProps = React.PropsWithChildren<BearProps>
function BearProvider({ children, ...props }: BearProviderProps) {
const storeRef = useRef<BearStore>()
if (!storeRef.current) {
storeRef.current = createBearStore(props)
}
return (
<BearContext.Provider value={storeRef.current}>
{children}
</BearContext.Provider>
)
}
```
### Extracting context logic into a custom hook
```tsx
// Mimic the hook returned by `create`
import { useContext } from 'react'
import { useStore } from 'zustand'
function useBearContext<T>(
selector: (state: BearState) => T,
equalityFn?: (left: T, right: T) => boolean
): T {
const store = useContext(BearContext)
if (!store) throw new Error('Missing BearContext.Provider in the tree')
return useStore(store, selector, equalityFn)
}
```
```tsx
// Consumer usage of the custom hook
function CommonConsumer() {
const bears = useBearContext((s) => s.bears)
const addBear = useBearContext((s) => s.addBear)
return (
<>
<div>{bears} Bears.</div>
<button onClick={addBear}>Add bear</button>
</>
)
}
```
### Complete example
```tsx
// Provider wrapper & custom hook consumer
function App2() {
return (
<BearProvider bears={2}>
<HookConsumer />
</BearProvider>
)
}
```

View File

@ -1,6 +1,6 @@
---
title: Map and Set Usage
nav: 12
nav: 11
---
You need to wrap Maps and Sets inside an object. When you want its update to be reflected (e.g. in React),

View File

@ -1,6 +1,6 @@
---
title: Practice with no store actions
nav: 8
nav: 7
---
The recommended usage is to colocate actions and states within the store (let your actions be located together with your state).

View File

@ -1,7 +1,7 @@
---
title: Testing
description: How to test your new store
nav: 10
nav: 9
---
## Resetting state between tests in **react-dom**

View File

@ -1,6 +1,6 @@
---
title: TypeScript Guide
nav: 9
nav: 8
---
## Basic usage
@ -57,7 +57,7 @@ Here again, `x` is `unknown` instead of `string`.
<details>
<summary>More about the inference (just for the people curious and interested in TypeScript)</summary>
In some sense this inference failure is not a problem because a value of type `<T>(f: (t: T) => T) => T` cannot be written. That is to say you can't write the real runtime implementation of `createFoo`. Let's try it:
```js
@ -92,8 +92,8 @@ So we have two problems: lack of inference and unsoundness. Lack of inference ca
<summary>Why that currying `()(...)`?</summary>
<br/>
**TLDR**: It is a workaround for [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571).
**TLDR**: It is a workaround for [microsoft/TypeScript#10571](https://github.com/microsoft/TypeScript/issues/10571).
Imagine you have a scenario like this:

View File

@ -1,6 +1,6 @@
---
title: Updating nested state object values
nav: 4
nav: 3
---
## Deeply nested object

View File

@ -1,6 +1,6 @@
---
title: 3rd Party Libraries
nav: 14
nav: 16
---
Zustand provides bear necessities for state management which is great for most projects; however, some users wish to extend the library's feature set. This can be done using 3rd-party libraries created by the community.

View File

@ -1,6 +1,6 @@
---
title: Persist middleware
nav: 15
nav: 17
---
The persist middleware enables you to store your Zustand state in a storage (e.g. `localStorage`, `AsyncStorage`, `IndexedDB`, etc...) thus persisting it's data.

View File

@ -1,6 +1,6 @@
---
title: v4 Migrations
nav: 18
nav: 19
---
If you're not using the typed version (either via TypeScript or via JSDoc) then there are no breaking changes for you and hence no migration is needed either.

View File

@ -1,6 +1,6 @@
---
title: createContext from zustand/context
nav: 17
nav: 18
---
A special `createContext` is provided since v3.5,

View File

@ -1,7 +1,7 @@
---
title: Recipes
description: How to do all you need with Zustand
nav: 13
nav: 15
---
⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033

View File

@ -68,7 +68,7 @@
"build:shallow": "rollup -c --config-shallow",
"build:context": "rollup -c --config-context",
"postbuild": "yarn copy",
"prettier": "prettier '*.{js,json,md}' '{examples/src,src,tests,docs}/**/*.{js,jsx,ts,tsx,md,mdx}' --write",
"prettier": "prettier \"*.{js,json,md}\" \"{examples/src,src,tests,docs}/**/*.{js,jsx,ts,tsx,md,mdx}\" --write",
"prettier:ci": "prettier '*.{js,json,md}' '{examples/src,src,tests,docs}/**/*.{js,jsx,ts,tsx,md,mdx}' --list-different",
"eslint": "eslint --fix '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'",
"eslint:ci": "eslint '*.{js,json}' '{src,tests}/**/*.{ts,tsx}'",