Skip to content

Commit d460a9b

Browse files
committed
Docs updates
1 parent e5a987b commit d460a9b

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

content/docs/hooks-faq.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
3737
* [Can I run an effect only on updates?](#can-i-run-an-effect-only-on-updates)
3838
* [How to get the previous props or state?](#how-to-get-the-previous-props-or-state)
3939
* [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops)
40+
* [Is there something like forceUpdate?](#is-there-something-like-forceupdate)
4041
* [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component)
4142
* [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean)
4243
* **[Performance Optimizations](#performance-optimizations)**
@@ -322,6 +323,22 @@ function ScrollView({row}) {
322323

323324
This might look strange at first, but an update during rendering is exactly what `getDerivedStateFromProps` has always been like conceptually.
324325

326+
### Is there something like forceUpdate?
327+
328+
Both `useState` and `useReducer` Hooks [bail out of updates](/docs/hooks-reference.html#bailing-out-of-a-state-update) if the next value is the same as the previous one. Mutating state in place and calling `setState` will not cause a re-render.
329+
330+
Normally, you shouldn't mutate local state in React. However, as an escape hatch, you can use an incrementing counter to force a re-render even if the state has not changed:
331+
332+
```js
333+
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
334+
335+
function handleClick() {
336+
forceUpdate();
337+
}
338+
```
339+
340+
Try to avoid this pattern if possible.
341+
325342
### Can I make a ref to a function component?
326343

327344
While you shouldn't need this often, you may expose some imperative methods to a parent component with the [`useImperativeHandle`](/docs/hooks-reference.html#useimperativehandle) Hook.

content/docs/hooks-reference.md

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ const [state, setState] = useState(() => {
8989
});
9090
```
9191
92+
#### Bailing out of a state update
93+
94+
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
95+
9296
### `useEffect`
9397

9498
```js
@@ -172,20 +176,18 @@ The following Hooks are either variants of the basic ones from the previous sect
172176
### `useReducer`
173177

174178
```js
175-
const [state, dispatch] = useReducer(reducer, initialState);
179+
const [state, dispatch] = useReducer(reducer, initialArg, init);
176180
```
177181

178182
An alternative to [`useState`](#usestate). Accepts a reducer of type `(state, action) => newState`, and returns the current state paired with a `dispatch` method. (If you're familiar with Redux, you already know how this works.)
179183

184+
`useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because [you can pass `dispatch` down instead of callbacks](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down).
185+
180186
Here's the counter example from the [`useState`](#usestate) section, rewritten to use a reducer:
181187

182188
```js
183-
const initialState = {count: 0};
184-
185189
function reducer(state, action) {
186190
switch (action.type) {
187-
case 'reset':
188-
return initialState;
189191
case 'increment':
190192
return {count: state.count + 1};
191193
case 'decrement':
@@ -202,31 +204,59 @@ function Counter({initialCount}) {
202204
return (
203205
<>
204206
Count: {state.count}
205-
<button onClick={() => dispatch({type: 'reset'})}>
206-
Reset
207-
</button>
208207
<button onClick={() => dispatch({type: 'increment'})}>+</button>
209208
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
210209
</>
211210
);
212211
}
213212
```
214213

214+
#### Specifying the initial state
215+
216+
There’s a few different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way to pass the initial state as a second argument:
217+
218+
```js{3}
219+
const [state, dispatch] = useReducer(
220+
reducer,
221+
{count: initialCount}
222+
);
223+
```
224+
225+
>Note
226+
>
227+
>React doesn’t use the `state = initialState` argument convention popularized by Redux. The initial value sometimes needs to depend on props and so is specified from the Hook call instead. If you feel strongly about this, you can write `state = initialState` both in the reducer and inside the `useReducer` destructuring assignment, but it's not encouraged.
228+
215229
#### Lazy initialization
216230

217-
`useReducer` accepts an optional third argument, `initialAction`. If provided, the initial action is applied during the initial render. This is useful for computing an initial state that includes values passed via props:
231+
If calculating the initial state is expensive, you can initialize it lazily. In that case, you can skip the second argument (and pass `undefined`). The third `useReducer` argument is an optional `init` function that you can provide to calculate the initial value once:
218232

219-
```js
220-
const initialState = {count: 0};
233+
```js{3-4}
234+
const [state, dispatch] = useReducer(
235+
reducer,
236+
undefined,
237+
() => ({count: initialCount})
238+
);
239+
```
240+
241+
#### Lazy initialization with a transform
242+
243+
For the most flexibility, you can specify *both* the second `initialArg` and the third `init` function arguments. In that case, the initial state will be set to `init(initialArg)`.
244+
245+
This is handy if you want to extract the lazy initialization logic outside your reducer:
246+
247+
```js{1-3,11-12,21,26}
248+
function init(initialCount) {
249+
return {count: initialCount};
250+
}
221251
222252
function reducer(state, action) {
223253
switch (action.type) {
224-
case 'reset':
225-
return {count: action.payload};
226254
case 'increment':
227255
return {count: state.count + 1};
228256
case 'decrement':
229257
return {count: state.count - 1};
258+
case 'reset':
259+
return init(action.payload);
230260
default:
231261
// A reducer must always return a valid state.
232262
// Alternatively you can throw an error if an invalid action is dispatched.
@@ -235,12 +265,7 @@ function reducer(state, action) {
235265
}
236266
237267
function Counter({initialCount}) {
238-
const [state, dispatch] = useReducer(
239-
reducer,
240-
initialState,
241-
{type: 'reset', payload: initialCount},
242-
);
243-
268+
const [state, dispatch] = useReducer(reducer, initialCount, init);
244269
return (
245270
<>
246271
Count: {state.count}
@@ -255,7 +280,9 @@ function Counter({initialCount}) {
255280
}
256281
```
257282

258-
`useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values. It also lets you optimize performance for components that trigger deep updates because [you can pass `dispatch` down instead of callbacks](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down).
283+
#### Bailing out of a dispatch
284+
285+
If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
259286

260287
### `useCallback`
261288

0 commit comments

Comments
 (0)