Skip to content

Commit 45dfd45

Browse files
authoredFeb 22, 2021
Assorted docs cleanup (#1688)
1 parent a923553 commit 45dfd45

File tree

9 files changed

+5736
-5689
lines changed

9 files changed

+5736
-5689
lines changed
 

‎docs/api/hooks.md

+93-45
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ React's new ["hooks" APIs](https://reactjs.org/docs/hooks-intro.html) give funct
1111

1212
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()`.
1313

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.
19+
20+
:::
21+
1422
These hooks were first added in v7.1.0.
1523

1624
## Using Hooks in a React Redux App
@@ -38,7 +46,11 @@ const result: any = useSelector(selector: Function, equalityFn?: Function)
3846

3947
Allows you to extract data from the Redux store state, using a selector function.
4048

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+
:::
4254

4355
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.
4456

@@ -50,7 +62,11 @@ However, there are some differences between the selectors passed to `useSelector
5062
- Extra care must be taken when using memoizing selectors (see examples below for more details).
5163
- `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
5264

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+
:::
5470

5571
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.
5672

@@ -91,7 +107,7 @@ import React from 'react'
91107
import { useSelector } from 'react-redux'
92108

93109
export const CounterComponent = () => {
94-
const counter = useSelector((state) => state.counter)
110+
const counter = useSelector(state => state.counter)
95111
return <div>{counter}</div>
96112
}
97113
```
@@ -102,8 +118,8 @@ Using props via closure to determine what to extract:
102118
import React from 'react'
103119
import { useSelector } from 'react-redux'
104120

105-
export const TodoListItem = (props) => {
106-
const todo = useSelector((state) => state.todos[props.id])
121+
export const TodoListItem = props => {
122+
const todo = useSelector(state => state.todos[props.id])
107123
return <div>{todo.text}</div>
108124
}
109125
```
@@ -119,21 +135,21 @@ import React from 'react'
119135
import { useSelector } from 'react-redux'
120136
import { createSelector } from 'reselect'
121137

122-
const selectNumOfDoneTodos = createSelector(
123-
(state) => state.todos,
124-
(todos) => todos.filter((todo) => todo.isDone).length
138+
const selectNumCompletedTodos = createSelector(
139+
state => state.todos,
140+
todos => todos.filter(todo => todo.completed).length
125141
)
126142

127-
export const DoneTodosCounter = () => {
128-
const numOfDoneTodos = useSelector(selectNumOfDoneTodos)
129-
return <div>{numOfDoneTodos}</div>
143+
export const CompletedTodosCounter = () => {
144+
const numCompletedTodos = useSelector(selectNumCompletedTodos)
145+
return <div>{numCompletedTodos}</div>
130146
}
131147

132148
export const App = () => {
133149
return (
134150
<>
135-
<span>Number of done todos:</span>
136-
<DoneTodosCounter />
151+
<span>Number of completed todos:</span>
152+
<CompletedTodosCounter />
137153
</>
138154
)
139155
}
@@ -146,25 +162,26 @@ import React from 'react'
146162
import { useSelector } from 'react-redux'
147163
import { createSelector } from 'reselect'
148164

149-
const selectNumOfTodosWithIsDoneValue = createSelector(
150-
(state) => state.todos,
151-
(_, isDone) => isDone,
152-
(todos, isDone) => todos.filter((todo) => todo.isDone === isDone).length
165+
const selectCompletedTodosCount = createSelector(
166+
state => state.todos,
167+
(_, completed) => completed,
168+
(todos, completed) =>
169+
todos.filter(todo => todo.completed === completed).length
153170
)
154171

155-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
156-
const NumOfTodosWithIsDoneValue = useSelector((state) =>
157-
selectNumOfTodosWithIsDoneValue(state, isDone)
172+
export const CompletedTodosCount = ({ completed }) => {
173+
const matchingCount = useSelector(state =>
174+
selectCompletedTodosCount(state, completed)
158175
)
159176

160-
return <div>{NumOfTodosWithIsDoneValue}</div>
177+
return <div>{matchingCount}</div>
161178
}
162179

163180
export const App = () => {
164181
return (
165182
<>
166183
<span>Number of done todos:</span>
167-
<TodoCounterForIsDoneValue isDone={true} />
184+
<CompletedTodosCount completed={true} />
168185
</>
169186
)
170187
}
@@ -177,40 +194,36 @@ import React, { useMemo } from 'react'
177194
import { useSelector } from 'react-redux'
178195
import { createSelector } from 'reselect'
179196

180-
const makeNumOfTodosWithIsDoneSelector = () =>
197+
const makeSelectCompletedTodosCount = () =>
181198
createSelector(
182-
(state) => state.todos,
183-
(_, isDone) => isDone,
184-
(todos, isDone) => todos.filter((todo) => todo.isDone === isDone).length
199+
state => state.todos,
200+
(_, completed) => completed,
201+
(todos, completed) =>
202+
todos.filter(todo => todo.completed === completed).length
185203
)
186204

187-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
188-
const selectNumOfTodosWithIsDone = useMemo(
189-
makeNumOfTodosWithIsDoneSelector,
190-
[]
191-
)
205+
export const CompletedTodosCount = ({ completed }) => {
206+
const selectCompletedTodosCount = useMemo(makeSelectCompletedTodosCount, [])
192207

193-
const numOfTodosWithIsDoneValue = useSelector((state) =>
194-
selectNumOfTodosWithIsDone(state, isDone)
208+
const matchingCount = useSelector(state =>
209+
selectCompletedTodosCount(state, completed)
195210
)
196211

197-
return <div>{numOfTodosWithIsDoneValue}</div>
212+
return <div>{matchingCount}</div>
198213
}
199214

200215
export const App = () => {
201216
return (
202217
<>
203218
<span>Number of done todos:</span>
204-
<TodoCounterForIsDoneValue isDone={true} />
219+
<CompletedTodosCount completed={true} />
205220
<span>Number of unfinished todos:</span>
206-
<TodoCounterForIsDoneValue isDone={false} />
221+
<CompletedTodosCount completed={false} />
207222
</>
208223
)
209224
}
210225
```
211226

212-
## Removed: `useActions()`
213-
214227
## `useDispatch()`
215228

216229
```js
@@ -219,8 +232,6 @@ const dispatch = useDispatch()
219232

220233
This hook returns a reference to the `dispatch` function from the Redux store. You may use it to dispatch actions as needed.
221234

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)._
223-
224235
#### Examples
225236

226237
```jsx
@@ -241,7 +252,7 @@ export const CounterComponent = ({ value }) => {
241252
}
242253
```
243254

244-
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.
245256

246257
```jsx
247258
import React, { useCallback } from 'react'
@@ -267,6 +278,28 @@ export const MyIncrementButton = React.memo(({ onIncrement }) => (
267278
))
268279
```
269280

281+
:::info
282+
283+
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+
export const Todos() = () => {
291+
const dispatch = 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+
270303
## `useStore()`
271304

272305
```js
@@ -304,7 +337,7 @@ import {
304337
Provider,
305338
createStoreHook,
306339
createDispatchHook,
307-
createSelectorHook,
340+
createSelectorHook
308341
} from 'react-redux'
309342
310343
const MyContext = React.createContext(null)
@@ -329,6 +362,12 @@ export function MyProvider({ children }) {
329362
330363
### Stale Props and "Zombie Children"
331364
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+
332371
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.
333372
334373
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
358397
- 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`.
359398
- 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.
360399

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)
406+
- [issue #1179](https://github.com/reduxjs/react-redux/issues/1179)
407+
408+
:::
362409

363410
### Performance
364411

@@ -368,7 +415,7 @@ If further performance optimizations are necessary, you may consider wrapping yo
368415

369416
```jsx
370417
const CounterComponent = ({ name }) => {
371-
const counter = useSelector((state) => state.counter)
418+
const counter = useSelector(state => state.counter)
372419
return (
373420
<div>
374421
{name}: {counter}
@@ -409,7 +456,7 @@ export function useActions(actions, deps) {
409456
return useMemo(
410457
() => {
411458
if (Array.isArray(actions)) {
412-
return actions.map((a) => bindActionCreators(a, dispatch))
459+
return actions.map(a => bindActionCreators(a, dispatch))
413460
}
414461
return bindActionCreators(actions, dispatch)
415462
},
@@ -431,3 +478,4 @@ export function useShallowEqualSelector(selector) {
431478
### Additional considerations when using hooks
432479
433480
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/).
481+
````

‎website/.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# dependencies
2+
/node_modules
3+
4+
# production
5+
/build
6+
7+
# generated files
8+
.docusaurus/
9+
website/.docusaurus/
10+
.cache-loader
11+
12+
# misc
13+
.DS_Store
14+
.env.local
15+
.env.development.local
16+
.env.test.local
17+
.env.production.local
18+
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*

‎website/docusaurus.config.js

+42-42
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ const siteConfig = {
2020
routeBasePath: '/',
2121
include: [
2222
'{api,introduction,using-react-redux}/*.{md,mdx}',
23-
'troubleshooting.md',
24-
], // no other way to exclude node_modules
23+
'troubleshooting.md'
24+
] // no other way to exclude node_modules
2525
},
2626
theme: {
2727
customCss: [
2828
require.resolve('./static/css/custom.css'),
2929
require.resolve('./static/css/404.css'),
30-
require.resolve('./static/css/codeblock.css'),
31-
],
32-
},
33-
},
34-
],
30+
require.resolve('./static/css/codeblock.css')
31+
]
32+
}
33+
}
34+
]
3535
],
3636
title: 'React Redux', // Title for your website.
3737
onBrokenLinks: 'throw',
@@ -70,14 +70,14 @@ const siteConfig = {
7070
{
7171
src:
7272
'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
73-
async: true,
74-
},
73+
async: true
74+
}
7575
],
7676

7777
// You may provide arbitrary config keys to be used as needed by your
7878
// template. For example, if you need your repo's URL...
7979
customFields: {
80-
repoUrl: 'https://github.com/reduxjs/react-redux',
80+
repoUrl: 'https://github.com/reduxjs/react-redux'
8181
},
8282
/**
8383
* Note:
@@ -89,50 +89,50 @@ const siteConfig = {
8989
themeConfig: {
9090
metadatas: [{ name: 'twitter:card', content: 'summary' }],
9191
prism: {
92-
theme: require('./static/scripts/monokaiTheme.js'),
92+
theme: require('./static/scripts/monokaiTheme.js')
9393
},
9494
image: 'img/redux-logo-landscape.png',
9595
navbar: {
9696
title: 'React Redux',
9797
logo: {
9898
alt: 'Redux Logo',
99-
src: 'img/redux.svg',
99+
src: 'img/redux.svg'
100100
},
101101
items: [
102102
{
103103
type: 'docsVersionDropdown',
104-
position: 'left',
104+
position: 'left'
105105
// Do not add the link active class when browsing docs.
106106
},
107107
{
108108
to: 'introduction/quick-start',
109109
label: 'Quick Start',
110-
position: 'right',
110+
position: 'right'
111111
},
112112
{
113113
to: 'using-react-redux/connect-mapstate',
114114
label: 'Using React Redux',
115-
position: 'right',
115+
position: 'right'
116116
},
117-
{ to: 'api/connect', label: 'API', position: 'right' },
117+
{ to: 'api/hooks', label: 'API', position: 'right' },
118118
{
119119
href: 'https://www.github.com/reduxjs/react-redux',
120120
label: 'GitHub',
121121
position: 'right',
122-
className: 'github',
122+
className: 'github'
123123
},
124124
{
125125
href: '/introduction/quick-start#help-and-discussion',
126126
label: 'Need help?',
127-
position: 'right',
128-
},
129-
],
127+
position: 'right'
128+
}
129+
]
130130
},
131131
footer: {
132132
style: 'dark',
133133
logo: {
134134
alt: 'Redux Logo',
135-
src: 'img/redux_white.svg',
135+
src: 'img/redux_white.svg'
136136
},
137137
copyright:
138138
'Copyright (c) 2015-present Dan Abramov and the Redux documentation authors.',
@@ -142,41 +142,41 @@ const siteConfig = {
142142
items: [
143143
{
144144
label: 'Introduction',
145-
to: 'introduction/quick-start',
145+
to: 'introduction/quick-start'
146146
},
147147
{
148148
label: 'Using React Redux',
149-
to: 'using-react-redux/connect-mapstate',
149+
to: 'using-react-redux/connect-mapstate'
150150
},
151151
{
152152
label: 'API Reference',
153-
to: 'api/connect',
153+
to: 'api/hooks'
154154
},
155155
{
156156
label: 'Guides',
157-
to: 'troubleshooting',
158-
},
159-
],
157+
to: 'troubleshooting'
158+
}
159+
]
160160
},
161161
{
162162
title: 'Community',
163163
items: [
164164
{
165165
label: 'Stack Overflow',
166-
href: 'https://stackoverflow.com/questions/tagged/react-redux',
166+
href: 'https://stackoverflow.com/questions/tagged/react-redux'
167167
},
168168
{
169169
label: 'Discord',
170-
href: 'https://discord.gg/0ZcbPKXt5bZ6au5t',
171-
},
172-
],
170+
href: 'https://discord.gg/0ZcbPKXt5bZ6au5t'
171+
}
172+
]
173173
},
174174
{
175175
title: 'More',
176176
items: [
177177
{
178178
label: 'GitHub',
179-
href: 'https://github.com/reduxjs/react-redux',
179+
href: 'https://github.com/reduxjs/react-redux'
180180
},
181181
{
182182
html: `
@@ -191,7 +191,7 @@ const siteConfig = {
191191
>
192192
Star
193193
</a>
194-
`,
194+
`
195195
},
196196
{
197197
html: `
@@ -201,21 +201,21 @@ const siteConfig = {
201201
alt="Deploys by Netlify"
202202
/>
203203
</a>
204-
`,
205-
},
206-
],
207-
},
208-
],
204+
`
205+
}
206+
]
207+
}
208+
]
209209
},
210210
algolia: {
211211
apiKey: '2d058d216b7fd5d68d481fd48ee72c06',
212212
indexName: 'react-redux',
213-
algoliaOptions: {},
213+
algoliaOptions: {}
214214
},
215215
googleAnalytics: {
216-
trackingID: 'UA-130598673-2',
217-
},
218-
},
216+
trackingID: 'UA-130598673-2'
217+
}
218+
}
219219
}
220220

221221
module.exports = siteConfig

‎website/package-lock.json

+5,335-5,473
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎website/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"serve": "docusaurus serve"
1010
},
1111
"devDependencies": {
12-
"@docusaurus/core": "2.0.0-alpha.65",
13-
"@docusaurus/preset-classic": "2.0.0-alpha.65",
12+
"@docusaurus/core": "2.0.0-alpha.70",
13+
"@docusaurus/preset-classic": "2.0.0-alpha.70",
1414
"classnames": "^2.2.6",
1515
"react": "^16.10.2",
1616
"react-dom": "^16.10.2"

‎website/src/pages/index.js

+26-44
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ const features = [
1111
{
1212
content: (
1313
<p>
14-
React Redux is maintained by the Redux team, and <strong>kept up-to-date with the latest APIs from Redux and React</strong>.
14+
React Redux is maintained by the Redux team, and{' '}
15+
<strong>
16+
kept up-to-date with the latest APIs from Redux and React
17+
</strong>
18+
.
1519
</p>
1620
),
1721
image: <img src="img/noun_Certificate_1945625.svg" />,
@@ -21,8 +25,9 @@ const features = [
2125
{
2226
content: (
2327
<p>
24-
<strong>Designed to work with React's component model</strong>.
25-
{' '} You define how to extract the values your component needs from Redux, and your component receives them as props.
28+
<strong>Designed to work with React's component model</strong>. You
29+
define how to extract the values your component needs from Redux, and
30+
your component receives them as props.
2631
</p>
2732
),
2833
image: <img src="img/noun_Check_1870817.svg" />,
@@ -32,7 +37,9 @@ const features = [
3237
{
3338
content: (
3439
<p>
35-
Creates wrapper components that <strong>manage the store interaction logic for you</strong>, so you don't have to write it yourself.
40+
Creates wrapper components that{' '}
41+
<strong>manage the store interaction logic for you</strong>, so you
42+
don't have to write it yourself.
3643
</p>
3744
),
3845
image: <img src="img/noun_Box_1664404.svg" />,
@@ -42,13 +49,15 @@ const features = [
4249
{
4350
content: (
4451
<p>
45-
Automatically implements <strong>complex performance optimizations</strong>, so that your own component only re-renders when the data it needs has actually changed.
52+
Automatically implements{' '}
53+
<strong>complex performance optimizations</strong>, so that your own
54+
component only re-renders when the data it needs has actually changed.
4655
</p>
4756
),
4857
image: <img src="img/noun_Rocket_1245262.svg" />,
4958
imageAlign: 'top',
5059
title: 'Optimized'
51-
},
60+
}
5261
]
5362

5463
const otherLibraries = [
@@ -66,10 +75,11 @@ const otherLibraries = [
6675
>
6776
<path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
6877
</svg>
69-
),
78+
)
7079
},
7180
{
72-
content: 'The official, opinionated, batteries-included toolset for efficient Redux development',
81+
content:
82+
'The official, opinionated, batteries-included toolset for efficient Redux development',
7383
title: 'Redux Toolkit',
7484
link: 'https://redux-toolkit.js.org',
7585
image: (
@@ -82,50 +92,22 @@ const otherLibraries = [
8292
>
8393
<path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
8494
</svg>
85-
),
86-
},
95+
)
96+
}
8797
]
8898

8999
function Home() {
90100
const context = useDocusaurusContext()
91101
const { siteConfig = {} } = context
92102
useEffect(() => {
93-
const script = document.createElement("script");
94-
script.src = "https://buttons.github.io/buttons.js";
95-
script.async = true;
96-
document.body.appendChild(script);
103+
const script = document.createElement('script')
104+
script.src = 'https://buttons.github.io/buttons.js'
105+
script.async = true
106+
document.body.appendChild(script)
97107
}, [])
98108

99109
return (
100110
<Layout title={siteConfig.title} description={siteConfig.tagline}>
101-
<div style={{ background: '#111', padding: '10px 0', lineHeight: 2 }}>
102-
<div className="container">
103-
<div
104-
style={{
105-
color: 'white',
106-
fontWeight: 'bold',
107-
textAlign: 'center'
108-
}}
109-
>
110-
Black Lives Matter.
111-
<a
112-
style={{
113-
display: 'inline-block',
114-
color: 'white',
115-
fontWeight: 'bold',
116-
margin: '0 10px',
117-
padding: '7px 20px',
118-
border: '1px solid white'
119-
}}
120-
href="https://support.eji.org/give/153413"
121-
target="_blank"
122-
rel="noopener noreferrer"
123-
>
124-
Support the Equal Justice Initiative.
125-
</a>
126-
</div>
127-
</div>
128-
</div>
129111
<header className={classnames('hero hero--primary', styles.heroBanner)}>
130112
<div className="container">
131113
<div className={styles.title}>
@@ -144,7 +126,7 @@ function Home() {
144126
<Link
145127
className={classnames(
146128
'button button--secondary button--lg',
147-
styles.getStarted,
129+
styles.getStarted
148130
)}
149131
to={useBaseUrl('introduction/quick-start')}
150132
>
@@ -209,4 +191,4 @@ function Home() {
209191
)
210192
}
211193

212-
export default Home;
194+
export default Home

‎website/static/css/custom.css

+36-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
/* your custom css */
1+
/**
2+
* Any CSS included here will be global. The classic template
3+
* bundles Infima by default. Infima is a CSS framework designed to
4+
* work well for content-centric websites.
5+
*/
6+
/* You can override the default Infima variables here. */
27
:root {
38
--ifm-color-primary: #764abc;
49
--ifm-color-primary-dark: #6a43a9;
@@ -14,6 +19,7 @@
1419

1520
--ifm-blockquote-color: #ecf4f9;
1621
--ifm-blockquote-color-dark: #cbddea;
22+
--blockquote-text-color: var(--ifm-font-base-color);
1723

1824
--ifm-code-padding-vertical: 0.1rem;
1925
--ifm-code-padding-horizontal: 0.2rem;
@@ -61,24 +67,22 @@
6167
--ifm-color-primary-light: #b97cfd;
6268
--ifm-color-primary-lighter: #cc8ffc;
6369
--ifm-color-primary-lightest: #fcf2ff;
64-
--ifm-button-color: #ffffff;
65-
--ifm-blockquote-color: #1a1d1e;
70+
71+
--ifm-blockquote-color: #ecf4f9;
6672
--ifm-blockquote-color-dark: #6d1cac;
73+
--blockquote-text-color: black;
6774
}
6875

6976
:root[data-theme='dark'] .hero.hero--primary {
7077
--ifm-hero-background-color: #593d88;
7178
--ifm-hero-text-color: #ffffff;
7279
}
7380
blockquote {
74-
color: var(--ifm-font-base-color);
81+
color: var(--blockquote-text-color);
7582
background-color: var(--ifm-blockquote-color);
7683
border-left: 6px solid var(--ifm-blockquote-color-dark);
7784
border-radius: var(--ifm-global-radius);
78-
}
7985

80-
html[data-theme='dark'] blockquote {
81-
border-left-color: var(--ifm-color-primary);
8286
}
8387

8488
.docusaurus-highlight-code-line {
@@ -123,10 +127,27 @@ html[data-theme='dark'] blockquote {
123127
border-left-color: var(--ifm-color-primary);
124128
}
125129

130+
a:visited {
131+
color: var(--ifm-color-primary);
132+
}
133+
126134
.navbar__items .dropdown .navbar__link--active {
127135
color: var(--ifm-navbar-link-color);
128136
}
129137

138+
.navbar .navbar__inner {
139+
flex-wrap: nowrap;
140+
}
141+
142+
.navbar .navbar__items {
143+
flex: 1 1 auto;
144+
}
145+
146+
.footer__logo {
147+
width: 50px;
148+
height: 50px;
149+
}
150+
130151
.menu__link {
131152
font-weight: normal;
132153
}
@@ -152,6 +173,14 @@ html[data-theme='dark'] blockquote {
152173
transform: rotateZ(90deg);
153174
}
154175

176+
.codesandbox {
177+
width: 100%;
178+
height: 500px;
179+
border: 0;
180+
border-radius: 4px;
181+
overflow: hidden;
182+
}
183+
155184
.admonition {
156185
color: black;
157186
border-radius: var(--ifm-global-radius);

‎website/versioned_docs/version-7.1/api/hooks.md

+99-42
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ React's new ["hooks" APIs](https://reactjs.org/docs/hooks-intro.html) give funct
1111

1212
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()`.
1313

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.
19+
20+
:::
21+
1422
These hooks were first added in v7.1.0.
1523

1624
## Using Hooks in a React Redux App
@@ -33,24 +41,32 @@ From there, you may import any of the listed React Redux hooks APIs and use them
3341
## `useSelector()`
3442

3543
```js
36-
const result : any = useSelector(selector : Function, equalityFn? : Function)
44+
const result: any = useSelector(selector: Function, equalityFn?: Function)
3745
```
3846

3947
Allows you to extract data from the Redux store state, using a selector function.
4048

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+
:::
4254

4355
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.
4456

4557
However, there are some differences between the selectors passed to `useSelector()` and a `mapState` function:
4658

4759
- 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.
48-
- 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, the component will not re-render.
60+
- 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.
4961
- 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.
5062
- Extra care must be taken when using memoizing selectors (see examples below for more details).
5163
- `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
5264

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+
:::
5470

5571
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.
5672

@@ -119,21 +135,21 @@ import React from 'react'
119135
import { useSelector } from 'react-redux'
120136
import { createSelector } from 'reselect'
121137

122-
const selectNumOfDoneTodos = createSelector(
138+
const selectNumCompletedTodos = createSelector(
123139
state => state.todos,
124-
todos => todos.filter(todo => todo.isDone).length
140+
todos => todos.filter(todo => todo.completed).length
125141
)
126142

127-
export const DoneTodosCounter = () => {
128-
const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
129-
return <div>{NumOfDoneTodos}</div>
143+
export const CompletedTodosCounter = () => {
144+
const numCompletedTodos = useSelector(selectNumCompletedTodos)
145+
return <div>{numCompletedTodos}</div>
130146
}
131147

132148
export const App = () => {
133149
return (
134150
<>
135-
<span>Number of done todos:</span>
136-
<DoneTodosCounter />
151+
<span>Number of completed todos:</span>
152+
<CompletedTodosCounter />
137153
</>
138154
)
139155
}
@@ -146,25 +162,26 @@ import React from 'react'
146162
import { useSelector } from 'react-redux'
147163
import { createSelector } from 'reselect'
148164

149-
const selectNumOfTodosWithIsDoneValue = createSelector(
165+
const selectCompletedTodosCount = createSelector(
150166
state => state.todos,
151-
(_, isDone) => isDone,
152-
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
167+
(_, completed) => completed,
168+
(todos, completed) =>
169+
todos.filter(todo => todo.completed === completed).length
153170
)
154171

155-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
156-
const NumOfTodosWithIsDoneValue = useSelector(state =>
157-
selectNumOfTodosWithIsDoneValue(state, isDone)
172+
export const CompletedTodosCount = ({ completed }) => {
173+
const matchingCount = useSelector(state =>
174+
selectCompletedTodosCount(state, completed)
158175
)
159176

160-
return <div>{NumOfTodosWithIsDoneValue}</div>
177+
return <div>{matchingCount}</div>
161178
}
162179

163180
export const App = () => {
164181
return (
165182
<>
166183
<span>Number of done todos:</span>
167-
<TodoCounterForIsDoneValue isDone={true} />
184+
<CompletedTodosCount completed={true} />
168185
</>
169186
)
170187
}
@@ -177,38 +194,37 @@ import React, { useMemo } from 'react'
177194
import { useSelector } from 'react-redux'
178195
import { createSelector } from 'reselect'
179196

180-
const makeNumOfTodosWithIsDoneSelector = () =>
197+
const makeSelectCompletedTodosCount = () =>
181198
createSelector(
182199
state => state.todos,
183-
(_, isDone) => isDone,
184-
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
200+
(_, completed) => completed,
201+
(todos, completed) =>
202+
todos.filter(todo => todo.completed === completed).length
185203
)
186204

187-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
188-
const selectNumOfTodosWithIsDone = useMemo(
189-
makeNumOfTodosWithIsDoneSelector,
190-
[]
191-
)
205+
export const CompletedTodosCount = ({ completed }) => {
206+
const selectCompletedTodosCount = useMemo(makeSelectCompletedTodosCount, [])
192207

193-
const numOfTodosWithIsDoneValue = useSelector(state =>
194-
selectNumOfTodosWithIsDoneValue(state, isDone)
208+
const matchingCount = useSelector(state =>
209+
selectCompletedTodosCount(state, completed)
195210
)
196211

197-
return <div>{numOfTodosWithIsDoneValue}</div>
212+
return <div>{matchingCount}</div>
198213
}
199214

200215
export const App = () => {
201216
return (
202217
<>
203218
<span>Number of done todos:</span>
204-
<TodoCounterForIsDoneValue isDone={true} />
219+
<CompletedTodosCount completed={true} />
205220
<span>Number of unfinished todos:</span>
206-
<TodoCounterForIsDoneValue isDone={false} />
221+
<CompletedTodosCount completed={false} />
207222
</>
208223
)
209224
}
210225
```
211226

227+
212228
## `useDispatch()`
213229

214230
```js
@@ -217,7 +233,6 @@ const dispatch = useDispatch()
217233

218234
This hook returns a reference to the `dispatch` function from the Redux store. You may use it to dispatch actions as needed.
219235

220-
*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).*
221236

222237
#### Examples
223238

@@ -239,7 +254,7 @@ export const CounterComponent = ({ value }) => {
239254
}
240255
```
241256

242-
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:
257+
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.
243258

244259
```jsx
245260
import React, { useCallback } from 'react'
@@ -265,6 +280,27 @@ export const MyIncrementButton = React.memo(({ onIncrement }) => (
265280
))
266281
```
267282

283+
:::info
284+
285+
The `dispatch` function reference will be stable as long as the same store instance is being passed to the `<Provider>`.
286+
Normally, that store instance never changes in an application.
287+
288+
However, the React hooks lint rules do not know that `dispatch` should be stable, and will warn that the `dispatch` variable
289+
should be added to dependency arrays for `useEffect` and `useCallback`. The simplest solution is to do just that:
290+
291+
````js
292+
export const Todos() = () => {
293+
const dispatch = useDispatch();
294+
295+
useEffect(() => {
296+
dispatch(fetchTodos())
297+
// highlight-start
298+
// Safe to add dispatch to the dependencies array
299+
}, [dispatch])
300+
// highlight-end
301+
}
302+
303+
:::
268304
## `useStore()`
269305

270306
```js
@@ -294,6 +330,12 @@ export const CounterComponent = ({ value }) => {
294330

295331
### Stale Props and "Zombie Children"
296332

333+
:::info
334+
335+
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**.
336+
337+
:::
338+
297339
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.
298340

299341
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.
@@ -323,7 +365,15 @@ If you prefer to deal with this issue yourself, here are some possible options f
323365
- 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`.
324366
- 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.
325367

326-
> **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).
368+
:::info
369+
370+
For a longer description of these scenarios, see:
371+
372+
- ["Stale props and zombie children in Redux" by Kai Hao](https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux)
373+
- [this chat log that describes the problems in more detail](https://gist.github.com/markerikson/faac6ae4aca7b82a058e13216a7888ec)
374+
- [issue #1179](https://github.com/reduxjs/react-redux/issues/1179)
375+
376+
:::
327377

328378
### Performance
329379

@@ -371,21 +421,28 @@ import { useMemo } from 'react'
371421

372422
export function useActions(actions, deps) {
373423
const dispatch = useDispatch()
374-
return useMemo(() => {
375-
if (Array.isArray(actions)) {
376-
return actions.map(a => bindActionCreators(a, dispatch))
377-
}
378-
return bindActionCreators(actions, dispatch)
379-
}, deps ? [dispatch, ...deps] : deps)
424+
return useMemo(
425+
() => {
426+
if (Array.isArray(actions)) {
427+
return actions.map(a => bindActionCreators(a, dispatch))
428+
}
429+
return bindActionCreators(actions, dispatch)
430+
},
431+
deps ? [dispatch, ...deps] : [dispatch]
432+
)
380433
}
381434
```
382435
383436
### Recipe: `useShallowEqualSelector()`
384437
385438
```js
386-
import { shallowEqual } from 'react-redux'
439+
import { useSelector, shallowEqual } from 'react-redux'
387440

388441
export function useShallowEqualSelector(selector) {
389442
return useSelector(selector, shallowEqual)
390443
}
391444
```
445+
446+
### Additional considerations when using hooks
447+
448+
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/).

‎website/versioned_docs/version-7.2/api/hooks.md

+82-34
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ React's new ["hooks" APIs](https://reactjs.org/docs/hooks-intro.html) give funct
1111

1212
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()`.
1313

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.
19+
20+
:::
21+
1422
These hooks were first added in v7.1.0.
1523

1624
## Using Hooks in a React Redux App
@@ -38,7 +46,11 @@ const result: any = useSelector(selector: Function, equalityFn?: Function)
3846

3947
Allows you to extract data from the Redux store state, using a selector function.
4048

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+
:::
4254

4355
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.
4456

@@ -50,7 +62,11 @@ However, there are some differences between the selectors passed to `useSelector
5062
- Extra care must be taken when using memoizing selectors (see examples below for more details).
5163
- `useSelector()` uses strict `===` reference equality checks by default, not shallow equality (see the following section for more details).
5264

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+
:::
5470

5571
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.
5672

@@ -119,21 +135,21 @@ import React from 'react'
119135
import { useSelector } from 'react-redux'
120136
import { createSelector } from 'reselect'
121137

122-
const selectNumOfDoneTodos = createSelector(
138+
const selectNumCompletedTodos = createSelector(
123139
state => state.todos,
124-
todos => todos.filter(todo => todo.isDone).length
140+
todos => todos.filter(todo => todo.completed).length
125141
)
126142

127-
export const DoneTodosCounter = () => {
128-
const numOfDoneTodos = useSelector(selectNumOfDoneTodos)
129-
return <div>{numOfDoneTodos}</div>
143+
export const CompletedTodosCounter = () => {
144+
const numCompletedTodos = useSelector(selectNumCompletedTodos)
145+
return <div>{numCompletedTodos}</div>
130146
}
131147

132148
export const App = () => {
133149
return (
134150
<>
135-
<span>Number of done todos:</span>
136-
<DoneTodosCounter />
151+
<span>Number of completed todos:</span>
152+
<CompletedTodosCounter />
137153
</>
138154
)
139155
}
@@ -146,25 +162,26 @@ import React from 'react'
146162
import { useSelector } from 'react-redux'
147163
import { createSelector } from 'reselect'
148164

149-
const selectNumOfTodosWithIsDoneValue = createSelector(
165+
const selectCompletedTodosCount = createSelector(
150166
state => state.todos,
151-
(_, isDone) => isDone,
152-
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
167+
(_, completed) => completed,
168+
(todos, completed) =>
169+
todos.filter(todo => todo.completed === completed).length
153170
)
154171

155-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
156-
const numOfTodosWithIsDoneValue = useSelector(state =>
157-
selectNumOfTodosWithIsDoneValue(state, isDone)
172+
export const CompletedTodosCount = ({ completed }) => {
173+
const matchingCount = useSelector(state =>
174+
selectCompletedTodosCount(state, completed)
158175
)
159176

160-
return <div>{numOfTodosWithIsDoneValue}</div>
177+
return <div>{matchingCount}</div>
161178
}
162179

163180
export const App = () => {
164181
return (
165182
<>
166183
<span>Number of done todos:</span>
167-
<TodoCounterForIsDoneValue isDone={true} />
184+
<CompletedTodosCount completed={true} />
168185
</>
169186
)
170187
}
@@ -177,39 +194,36 @@ import React, { useMemo } from 'react'
177194
import { useSelector } from 'react-redux'
178195
import { createSelector } from 'reselect'
179196

180-
const makeNumOfTodosWithIsDoneSelector = () =>
197+
const makeSelectCompletedTodosCount = () =>
181198
createSelector(
182199
state => state.todos,
183-
(_, isDone) => isDone,
184-
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
200+
(_, completed) => completed,
201+
(todos, completed) =>
202+
todos.filter(todo => todo.completed === completed).length
185203
)
186204

187-
export const TodoCounterForIsDoneValue = ({ isDone }) => {
188-
const selectNumOfTodosWithIsDone = useMemo(
189-
makeNumOfTodosWithIsDoneSelector,
190-
[]
191-
)
205+
export const CompletedTodosCount = ({ completed }) => {
206+
const selectCompletedTodosCount = useMemo(makeSelectCompletedTodosCount, [])
192207

193-
const numOfTodosWithIsDoneValue = useSelector(state =>
194-
selectNumOfTodosWithIsDone(state, isDone)
208+
const matchingCount = useSelector(state =>
209+
selectCompletedTodosCount(state, completed)
195210
)
196211

197-
return <div>{numOfTodosWithIsDoneValue}</div>
212+
return <div>{matchingCount}</div>
198213
}
199214

200215
export const App = () => {
201216
return (
202217
<>
203218
<span>Number of done todos:</span>
204-
<TodoCounterForIsDoneValue isDone={true} />
219+
<CompletedTodosCount completed={true} />
205220
<span>Number of unfinished todos:</span>
206-
<TodoCounterForIsDoneValue isDone={false} />
221+
<CompletedTodosCount completed={false} />
207222
</>
208223
)
209224
}
210225
```
211226

212-
## Removed: `useActions()`
213227

214228
## `useDispatch()`
215229

@@ -219,7 +233,6 @@ const dispatch = useDispatch()
219233

220234
This hook returns a reference to the `dispatch` function from the Redux store. You may use it to dispatch actions as needed.
221235

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

224237
#### Examples
225238

@@ -241,7 +254,7 @@ export const CounterComponent = ({ value }) => {
241254
}
242255
```
243256

244-
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:
257+
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.
245258

246259
```jsx
247260
import React, { useCallback } from 'react'
@@ -267,6 +280,27 @@ export const MyIncrementButton = React.memo(({ onIncrement }) => (
267280
))
268281
```
269282

283+
:::info
284+
285+
The `dispatch` function reference will be stable as long as the same store instance is being passed to the `<Provider>`.
286+
Normally, that store instance never changes in an application.
287+
288+
However, the React hooks lint rules do not know that `dispatch` should be stable, and will warn that the `dispatch` variable
289+
should be added to dependency arrays for `useEffect` and `useCallback`. The simplest solution is to do just that:
290+
291+
````js
292+
export const Todos() = () => {
293+
const dispatch = useDispatch();
294+
295+
useEffect(() => {
296+
dispatch(fetchTodos())
297+
// highlight-start
298+
// Safe to add dispatch to the dependencies array
299+
}, [dispatch])
300+
// highlight-end
301+
}
302+
303+
:::
270304
## `useStore()`
271305

272306
```js
@@ -329,6 +363,12 @@ export function MyProvider({ children }) {
329363
330364
### Stale Props and "Zombie Children"
331365
366+
:::info
367+
368+
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**.
369+
370+
:::
371+
332372
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.
333373
334374
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 +398,15 @@ If you prefer to deal with this issue yourself, here are some possible options f
358398
- 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`.
359399
- 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.
360400

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).
401+
:::info
402+
403+
For a longer description of these scenarios, see:
404+
405+
- ["Stale props and zombie children in Redux" by Kai Hao](https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux)
406+
- [this chat log that describes the problems in more detail](https://gist.github.com/markerikson/faac6ae4aca7b82a058e13216a7888ec)
407+
- [issue #1179](https://github.com/reduxjs/react-redux/issues/1179)
408+
409+
:::
362410

363411
### Performance
364412

0 commit comments

Comments
 (0)
Please sign in to comment.