Skip to content

Commit

Permalink
Docs updates
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Feb 4, 2019
1 parent e5a987b commit d460a9b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 20 deletions.
17 changes: 17 additions & 0 deletions content/docs/hooks-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This page answers some of the frequently asked questions about [Hooks](/docs/hoo
* [Can I run an effect only on updates?](#can-i-run-an-effect-only-on-updates)
* [How to get the previous props or state?](#how-to-get-the-previous-props-or-state)
* [How do I implement getDerivedStateFromProps?](#how-do-i-implement-getderivedstatefromprops)
* [Is there something like forceUpdate?](#is-there-something-like-forceupdate)
* [Can I make a ref to a function component?](#can-i-make-a-ref-to-a-function-component)
* [What does const [thing, setThing] = useState() mean?](#what-does-const-thing-setthing--usestate-mean)
* **[Performance Optimizations](#performance-optimizations)**
Expand Down Expand Up @@ -322,6 +323,22 @@ function ScrollView({row}) {

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

### Is there something like forceUpdate?

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.

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:

```js
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

function handleClick() {
forceUpdate();
}
```

Try to avoid this pattern if possible.

### Can I make a ref to a function component?

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.
Expand Down
67 changes: 47 additions & 20 deletions content/docs/hooks-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ const [state, setState] = useState(() => {
});
```
#### Bailing out of a state update

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).)

### `useEffect`

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

```js
const [state, dispatch] = useReducer(reducer, initialState);
const [state, dispatch] = useReducer(reducer, initialArg, init);
```

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.)

`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).

Here's the counter example from the [`useState`](#usestate) section, rewritten to use a reducer:

```js
const initialState = {count: 0};

function reducer(state, action) {
switch (action.type) {
case 'reset':
return initialState;
case 'increment':
return {count: state.count + 1};
case 'decrement':
Expand All @@ -202,31 +204,59 @@ function Counter({initialCount}) {
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'reset'})}>
Reset
</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}
```

#### Specifying the initial state

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:

```js{3}
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);
```

>Note
>
>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.
#### Lazy initialization

`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:
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:

```js
const initialState = {count: 0};
```js{3-4}
const [state, dispatch] = useReducer(
reducer,
undefined,
() => ({count: initialCount})
);
```

#### Lazy initialization with a transform

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)`.

This is handy if you want to extract the lazy initialization logic outside your reducer:

```js{1-3,11-12,21,26}
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'reset':
return {count: action.payload};
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
Expand All @@ -235,12 +265,7 @@ function reducer(state, action) {
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(
reducer,
initialState,
{type: 'reset', payload: initialCount},
);

const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
Expand All @@ -255,7 +280,9 @@ function Counter({initialCount}) {
}
```

`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).
#### Bailing out of a dispatch

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).)

### `useCallback`

Expand Down

0 comments on commit d460a9b

Please sign in to comment.