You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/api/hooks.md
+93-45Lines changed: 93 additions & 45 deletions
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,14 @@ React's new ["hooks" APIs](https://reactjs.org/docs/hooks-intro.html) give funct
11
11
12
12
React Redux now offers a set of hook APIs as an alternative to the existing `connect()` Higher Order Component. These APIs allow you to subscribe to the Redux store and dispatch actions, without having to wrap your components in `connect()`.
13
13
14
+
:::tip
15
+
16
+
**We recommend using the React-Redux hooks API as the default approach in your React components.**
17
+
18
+
The existing `connect` API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript.
Allows you to extract data from the Redux store state, using a selector function.
40
48
41
-
> **Note**: The selector function should be [pure](https://en.wikipedia.org/wiki/Pure_function) since it is potentially executed multiple times and at arbitrary points in time.
49
+
:::info
50
+
51
+
The selector function should be [pure](https://en.wikipedia.org/wiki/Pure_function) since it is potentially executed multiple times and at arbitrary points in time.
52
+
53
+
:::
42
54
43
55
The selector is approximately equivalent to the [`mapStateToProps` argument to `connect`](../using-react-redux/connect-mapstate) conceptually. The selector will be called with the entire Redux store state as its only argument. The selector will be run whenever the function component renders (unless its reference hasn't changed since a previous render of the component so that a cached result can be returned by the hook without re-running the selector). `useSelector()` will also subscribe to the Redux store, and run your selector whenever an action is dispatched.
44
56
@@ -50,7 +62,11 @@ However, there are some differences between the selectors passed to `useSelector
50
62
- Extra care must be taken when using memoizing selectors (see examples below for more details).
51
63
-`useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
52
64
53
-
> **Note**: There are potential edge cases with using props in selectors that may cause errors. See the [Usage Warnings](#usage-warnings) section of this page for further details.
65
+
:::info
66
+
67
+
There are potential edge cases with using props in selectors that may cause issues. See the [Usage Warnings](#usage-warnings) section of this page for further details.
68
+
69
+
:::
54
70
55
71
You may call `useSelector()` multiple times within a single function component. Each call to `useSelector()` creates an individual subscription to the Redux store. Because of the React update batching behavior used in React Redux v7, a dispatched action that causes multiple `useSelector()`s in the same component to return new values _should_ only result in a single re-render.
56
72
@@ -91,7 +107,7 @@ import React from 'react'
91
107
import { useSelector } from'react-redux'
92
108
93
109
exportconstCounterComponent= () => {
94
-
constcounter=useSelector((state)=>state.counter)
110
+
constcounter=useSelector(state=>state.counter)
95
111
return<div>{counter}</div>
96
112
}
97
113
```
@@ -102,8 +118,8 @@ Using props via closure to determine what to extract:
This hook returns a reference to the `dispatch` function from the Redux store. You may use it to dispatch actions as needed.
221
234
222
-
_Note: like in [React's `useReducer`](https://reactjs.org/docs/hooks-reference.html#usereducer), the returned `dispatch` function identity is stable and won't change on re-renders (unless you change the `store` being passed to the `<Provider>`, which would be extremely unusual)._
Reminder: when passing a callback using `dispatch` to a child component, you should memoize it with [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback), just like you should memoize any passed callback. This avoids unnecessary rendering of child components due to the changed callback reference. You can safely pass `[dispatch]` in the dependency array for the `useCallback` call - since `dispatch` won't change, the callback will be reused properly (as it should). For example:
255
+
When passing a callback using `dispatch` to a child component, you may sometimes want to memoize it with [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback). _If_ the child component is trying to optimize render behavior using `React.memo()` or similar, this avoids unnecessary rendering of child components due to the changed callback reference.
The `dispatch` function reference will be stable as long as the same store instance is being passed to the `<Provider>`.
284
+
Normally, that store instance never changes in an application.
285
+
286
+
However, the React hooks lint rules do not know that `dispatch` should be stable, and will warn that the `dispatch` variable
287
+
should be added to dependency arrays for `useEffect` and `useCallback`. The simplest solution is to do just that:
288
+
289
+
````js
290
+
exportconstTodos() = () => {
291
+
constdispatch=useDispatch();
292
+
293
+
useEffect(() => {
294
+
dispatch(fetchTodos())
295
+
// highlight-start
296
+
// Safe to add dispatch to the dependencies array
297
+
}, [dispatch])
298
+
// highlight-end
299
+
}
300
+
301
+
:::
302
+
270
303
## `useStore()`
271
304
272
305
```js
@@ -304,7 +337,7 @@ import {
304
337
Provider,
305
338
createStoreHook,
306
339
createDispatchHook,
307
-
createSelectorHook,
340
+
createSelectorHook
308
341
} from 'react-redux'
309
342
310
343
const MyContext = React.createContext(null)
@@ -329,6 +362,12 @@ export function MyProvider({ children }) {
329
362
330
363
### Stale Props and "Zombie Children"
331
364
365
+
:::info
366
+
367
+
The React-Redux hooks API has been production-ready since we released it in v7.1.0, and **we recommend using the hooks API as the default approach in your components**. However, there are a couple of edge cases that can occur, and **we're documenting those so that you can be aware of them**.
368
+
369
+
:::
370
+
332
371
One of the most difficult aspects of React Redux's implementation is ensuring that if your `mapStateToProps` function is defined as `(state, ownProps)`, it will be called with the "latest" props every time. Up through version 4, there were recurring bugs reported involving edge case situations, such as errors thrown from a `mapState` function for a list item whose data had just been deleted.
333
372
334
373
Starting with version 5, React Redux has attempted to guarantee that consistency with `ownProps`. In version 7, that is implemented using a custom `Subscription` class internally in `connect()`, which forms a nested hierarchy. This ensures that connected components lower in the tree will only receive store update notifications once the nearest connected ancestor has been updated. However, this relies on each `connect()` instance overriding part of the internal React context, supplying its own unique `Subscription` instance to form that nesting, and rendering the `<ReactReduxContext.Provider>` with that new context value.
@@ -358,7 +397,15 @@ If you prefer to deal with this issue yourself, here are some possible options f
358
397
- In cases where you do rely on props in your selector function _and_ those props may change over time, _or_ the data you're extracting may be based on items that can be deleted, try writing the selector functions defensively. Don't just reach straight into `state.todos[props.id].name` - read `state.todos[props.id]` first, and verify that it exists before trying to read `todo.name`.
359
398
- Because `connect` adds the necessary `Subscription` to the context provider and delays evaluating child subscriptions until the connected component has re-rendered, putting a connected component in the component tree just above the component using `useSelector` will prevent these issues as long as the connected component gets re-rendered due to the same store update as the hooks component.
360
399
361
-
> **Note**: For a longer description of this issue, see ["Stale props and zombie children in Redux" by Kai Hao](https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux), [this chat log that describes the problems in more detail](https://gist.github.com/markerikson/faac6ae4aca7b82a058e13216a7888ec), and [issue #1179](https://github.com/reduxjs/react-redux/issues/1179).
400
+
:::info
401
+
402
+
For a longer description of these scenarios, see:
403
+
404
+
- ["Stale props and zombie children in Redux" by Kai Hao](https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux)
405
+
- [this chat log that describes the problems in more detail](https://gist.github.com/markerikson/faac6ae4aca7b82a058e13216a7888ec)
@@ -431,3 +478,4 @@ export function useShallowEqualSelector(selector) {
431
478
### Additional considerations when using hooks
432
479
433
480
There are some architectural trade offs to take into consideration when deciding whether to use hooks or not. Mark Erikson summarizes these nicely in his two blog posts [Thoughts on React Hooks, Redux, and Separation of Concerns](https://blog.isquaredsoftware.com/2019/07/blogged-answers-thoughts-on-hooks/) and [Hooks, HOCs, and Tradeoffs](https://blog.isquaredsoftware.com/2019/09/presentation-hooks-hocs-tradeoffs/).
0 commit comments