Skip to content

Commit 0549388

Browse files
committed
Update useSelector equality info and add hooks recipes
1 parent 4a17e69 commit 0549388

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

docs/api/batch.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ React's `unstable_batchedUpdate()` API allows any React updates in an event loop
1616
Since React-Redux needs to work in both ReactDOM and React Native environments, we've taken care of importing this API from the correct renderer at build time for our own use. We also now re-export this function publicly ourselves, renamed to `batch()`. You can use it to ensure that multiple actions dispatched outside of React only result in a single render update, like this:
1717

1818
```js
19-
import { batch } from "react-redux";
19+
import { batch } from 'react-redux'
2020

2121
function myThunk() {
22-
return (dispatch, getState) => {
23-
// should only result in one combined re-render, not two
24-
batch(() => {
25-
dispatch(increment());
26-
dispatch(increment());
27-
})
28-
}
22+
return (dispatch, getState) => {
23+
// should only result in one combined re-render, not two
24+
batch(() => {
25+
dispatch(increment())
26+
dispatch(increment())
27+
})
28+
}
2929
}
3030
```
3131

3232
## References
3333

34-
- [`unstable_batchedUpdate()` API from React](https://github.com/facebook/react/commit/b41883fc708cd24d77dcaa767cde814b50b457fe)
34+
- [`unstable_batchedUpdate()` API from React](https://github.com/facebook/react/commit/b41883fc708cd24d77dcaa767cde814b50b457fe)

docs/api/hooks.md

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ React Redux now offers a set of hook APIs as an alternative to the existing `con
1515
1616
These hooks were first added in v7.1.0.
1717

18-
This page reflects the latest alpha, which is currently **v7.1.0-alpha.4**.
18+
This page reflects the latest alpha, which is currently **v7.1.0-alpha.5**.
1919

2020
## Using Hooks in a React Redux App
2121

@@ -37,7 +37,7 @@ From there, you may import any of the listed React Redux hooks APIs and use them
3737
## `useSelector()`
3838

3939
```js
40-
const result : any = useSelector(selector : Function)
40+
const result : any = useSelector(selector : Function, equalityFn? : Function)
4141
```
4242

4343
Allows you to extract data from the Redux store state, using a selector function.
@@ -52,12 +52,41 @@ However, there are some differences between the selectors passed to `useSelector
5252
- When an action is dispatched, `useSelector()` will do a shallow comparison of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, they component will not re-render.
5353
- The selector function does _not_ receive an `ownProps` argument. However, props can be used through closure (see the examples below) or by using a curried selector.
5454
- Extra care must be taken when using memoizing selectors (see examples below for more details).
55+
- `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
5556

5657
> **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.
5758
5859
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.
5960

60-
#### Examples
61+
### Equality Comparisons and Updates
62+
63+
When the function component renders, the provided selector function will be called and its result will be returned
64+
from the `useSelector()` hook. (A cached result may be returned if the selector has been run and hasn't changed.)
65+
66+
However, when an action is dispatched to the Redux store, `useSelector()` only forces a re-render if the selector result
67+
appears to be different than the last result. As of v7.1.0-alpha.5, the default comparison is a strict `===` reference
68+
comparison. This is different than `connect()`, which uses shallow equality checks on the results of `mapState` calls
69+
to determine if re-rendering is needed. This has several implications on how you should use `useSelector()`.
70+
71+
With `mapState`, all individual fields were returned in a combined object. It didn't matter if the return object was
72+
a new reference or not - `connect()` just compared the individual fields. With `useSelector()`, returning a new object
73+
every time will _always_ force a re-render by default. If you want to retrieve multiple values from the store, you can:
74+
75+
- Call `useSelector()` multiple times, with each call returning a single field value
76+
- Use Reselect or a similar library to create a memoized selector that returns multiple values in one object, but
77+
only returns a new object when one of the values has changed.
78+
- Use the `shallowEqual` function from React-Redux as the `equalityFn` argument to `useSelector()`, like:
79+
80+
```js
81+
import { shallowEqual, useSelector } from 'react-redux'
82+
83+
// later
84+
const selectedData = useSelector(selectorReturningObject, shallowEqual)
85+
```
86+
87+
The optional comparison function also enables using something like Lodash's `_.isEqual()` or Immutable.js's comparison capabilities.
88+
89+
### `useSelector` Examples
6190

6291
Basic usage:
6392

@@ -83,7 +112,7 @@ export const TodoListItem = props => {
83112
}
84113
```
85114

86-
##### Using memoizing selectors
115+
#### Using memoizing selectors
87116

88117
When using `useSelector` with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered. This works as long as the selector does not maintain any state. However, memoizing selectors (e.g. created via `createSelector` from `reselect`) do have internal state, and therefore care must be taken when using them. Below you can find typical usage scenarios for memoizing selectors.
89118

@@ -351,3 +380,37 @@ export const CounterComponent = props => {
351380
return renderedChildren
352381
}
353382
```
383+
384+
## Hooks Recipes
385+
386+
We've pared down our hooks API from the original alpha release, focusing on a more minimal set of API primitives.
387+
However, you may still wish to use some of the approaches we tried in your own apps. These examples should be ready
388+
to copy and paste into your own codebase.
389+
390+
### Recipe: `useActions()`
391+
392+
```js
393+
import { bindActionCreators } from 'redux'
394+
import { useDispatch } from 'react-redux'
395+
import { useMemo } from 'react'
396+
397+
export function useActions(actions, deps) {
398+
const dispatch = useDispatch()
399+
return useMemo(() => {
400+
if (Array.isArray(actions)) {
401+
return actions.map(a => bindActionCreators(a, dispatch))
402+
}
403+
return bindActionCreators(actions, dispatch)
404+
}, deps)
405+
}
406+
```
407+
408+
### Recipe: `useShallowEqualSelector()`
409+
410+
```js
411+
import { shallowEqual } from 'react-redux'
412+
413+
export function useShallowEqualSelector(selector) {
414+
return useSelector(selector, shallowEqual)
415+
}
416+
```

0 commit comments

Comments
 (0)