Skip to content

Commit ebfaf5c

Browse files
committed
Update hooks docs with TS types and better descriptions
1 parent ac122db commit ebfaf5c

File tree

1 file changed

+60
-27
lines changed

1 file changed

+60
-27
lines changed

docs/api/hooks.md

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,24 @@ From there, you may import any of the listed React Redux hooks APIs and use them
4444

4545
## `useSelector()`
4646

47-
```js
48-
const result: any = useSelector(selector: Function, equalityFn?: Function)
47+
```ts
48+
type RootState = ReturnType<typeof store.getState>
49+
type SelectorFn = <Selected>(state: RootState) => Selected
50+
type EqualityFn = (a: any, b: any) => boolean
51+
export type StabilityCheck = 'never' | 'once' | 'always'
52+
53+
interface UseSelectorOptions {
54+
equalityFn?: EqualityFn
55+
stabilityCheck?: StabilityCheck
56+
}
57+
58+
const result: Selected = useSelector(
59+
selector: SelectorFunction,
60+
options?: EqualityFn | UseSelectorOptions
61+
)
4962
```
5063

51-
Allows you to extract data from the Redux store state, using a selector function.
64+
Allows you to extract data from the Redux store state for use in this component, using a selector function.
5265

5366
:::info
5467

@@ -58,24 +71,22 @@ See [Using Redux: Deriving Data with Selectors](https://redux.js.org/usage/deriv
5871

5972
:::
6073

61-
The selector is approximately equivalent to the [`mapStateToProps` argument to `connect`](../using-react-redux/connect-extracting-data-with-mapStateToProps.md) 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.
74+
The selector will be called with the entire Redux store state as its only argument. The selector may return any value as a result, including directly returning a value that was nested inside `state`, or deriving new values. The return value of the selector will be used as the return value of the `useSelector()` hook.
75+
76+
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.
77+
78+
When an action is dispatched, `useSelector()` will do a reference 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, the component will not re-render. `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
6279

63-
However, there are some differences between the selectors passed to `useSelector()` and a `mapState` function:
80+
The selector is approximately equivalent to the [`mapStateToProps` argument to `connect`](../using-react-redux/connect-extracting-data-with-mapStateToProps.md) conceptually.
6481

65-
- The selector may return any value as a result, not just an object. The return value of the selector will be used as the return value of the `useSelector()` hook.
66-
- When an action is dispatched, `useSelector()` will do a reference 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, the component will not re-render.
67-
- 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.
68-
- Extra care must be taken when using memoizing selectors (see examples below for more details).
69-
- `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
82+
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.
7083

7184
:::info
7285

7386
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.
7487

7588
:::
7689

77-
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.
78-
7990
### Equality Comparisons and Updates
8091

8192
When the function component renders, the provided selector function will be called and its result will be returned
@@ -98,10 +109,10 @@ every time will _always_ force a re-render by default. If you want to retrieve m
98109
```js
99110
import { shallowEqual, useSelector } from 'react-redux'
100111

101-
// later
112+
// Pass it as the second argument directly
102113
const selectedData = useSelector(selectorReturningObject, shallowEqual)
103114

104-
// or with object format
115+
// or pass it as the `equalityFn` field in the options argument
105116
const selectedData = useSelector(selectorReturningObject, {
106117
equalityFn: shallowEqual,
107118
})
@@ -249,15 +260,23 @@ export const App = () => {
249260

250261
### Development mode checks
251262

263+
`useSelector` runs some extra checks in development mode to watch for unexpected behavior. These checks do not run in production builds.
264+
265+
:::info
266+
267+
These checks were first added in v8.1.0
268+
269+
:::
270+
252271
#### Selector result stability
253272

254-
In development, an extra check is conducted on the passed selector. It runs the selector an extra time with the same parameter, and warns in console if it returns a different result (based on the `equalityFn` provided).
273+
In development, the provided selector function is run an extra time with the same parameter during the first call to `useSelector`, and warns in the console if the selector returns a different result (based on the `equalityFn` provided).
255274

256-
This is important, as a selector returning a materially different result with the same parameter will cause unnecessary rerenders.
275+
This is important, as a selector returning that returns a different result reference with the same parameter will cause unnecessary rerenders.
257276

258277
```ts
259-
// this selector will return a new object reference whenever called
260-
// meaning the component will rerender whenever *any* action is dispatched
278+
// this selector will return a new object reference whenever called,
279+
// which causes the component to rerender after *every* action is dispatched
261280
const { count, user } = useSelector((state) => ({
262281
count: state.count,
263282
user: state.user,
@@ -283,14 +302,20 @@ function Component() {
283302
}
284303
```
285304

286-
:::info
287-
This check is disabled for production environments.
288-
:::
305+
### Comparisons with `connect`
306+
307+
There are some differences between the selectors passed to `useSelector()` and a `mapState` function:
308+
309+
- The selector may return any value as a result, not just an object.
310+
- The selector normally _should_ return just a single value, and not an object. If you do return an object or an array, be sure to use a memoized selector to avoid unnecessary re-renders.
311+
- The selector function does _not_ receive an `ownProps` argument. However, props can be used through closure (see the examples above) or by using a curried selector.
312+
- You can use the `equalityFn` option to customize the comparison behavior
289313

290314
## `useDispatch()`
291315

292-
```js
293-
const dispatch = useDispatch()
316+
```ts
317+
import type { Dispatch } from 'redux'
318+
const dispatch: Dispatch = useDispatch()
294319
```
295320

296321
This hook returns a reference to the `dispatch` function from the Redux store. You may use it to dispatch actions as needed.
@@ -366,8 +391,9 @@ export const Todos = () => {
366391

367392
## `useStore()`
368393

369-
```js
370-
const store = useStore()
394+
```ts
395+
import type { Store } from 'redux'
396+
const store: Store = useStore()
371397
```
372398

373399
This hook returns a reference to the same Redux store that was passed in to the `<Provider>` component.
@@ -380,12 +406,19 @@ This hook should probably not be used frequently. Prefer `useSelector()` as your
380406
import React from 'react'
381407
import { useStore } from 'react-redux'
382408

383-
export const CounterComponent = ({ value }) => {
409+
export const ExampleComponent = ({ value }) => {
384410
const store = useStore()
385411

412+
const onClick = () => {
413+
// Not _recommended_, but safe
414+
// This avoids subscribing to the state via `useSelector`
415+
// Prefer moving this logic into a thunk instead
416+
const numTodos = store.getState().todos.length
417+
}
418+
386419
// EXAMPLE ONLY! Do not do this in a real app.
387420
// The component will not automatically update if the store state changes
388-
return <div>{store.getState()}</div>
421+
return <div>{store.getState().todos.length}</div>
389422
}
390423
```
391424

0 commit comments

Comments
 (0)