如何用 Redux 管理 React 中的状态

作者选择了 Creative Commons以作为 Write for Donations计划的一部分获得捐赠。

介绍

Redux是用于 JavaScriptReact应用的流行的数据存储,它遵循一个核心原则,即数据绑定应该以一个方向流动,并应作为一个单一的真理源存储。

Redux 根据几个概念运作. 首先, store 是一个单个对象,每个数据选择都有字段. 您通过发送一个 action 来更新数据,说明数据应该如何改变。

在小型应用程序中,您可能不需要一个全球数据存储库. 您可以使用 本地状态背景的组合来管理状态。

在本教程中,您将通过构建鸟类观察测试应用程序来在 React 应用程序中使用 Redux。用户将能够添加他们已经看到的鸟类,并在每次再次看到它时增加一只鸟类。

前提条件

步骤1 - 设置一个商店

在此步骤中,您将安装 Redux,并将其连接到您的 root 组件

首先,安装‘redux’和‘react-redux’。包 ‘redux’是框架无知的,它将连接你的行动和减少器。包 ‘react-redux’包含在React项目中运行Redux商店的绑定。你将使用代码从‘react-redux’发送你的组件的行动,并将数据从商店拖到你的组件中。

使用「npm」來安裝有以下命令的兩個包:

1npm install --save redux react-redux

当组件安装完成后,您将收到这样的输出,您的输出可能略有不同:

1[secondary_label Output]
2...
3+ redux@4.0.5
4+ react-redux@7.2.1
5added 2 packages from 1 contributor, updated 1 package and audited 1639 packages in 20.573s

现在你已经安装了套件,你需要将Redux连接到你的项目。 要使用Redux,你需要用提供商包装你的根部件,以确保商店可用于树上的所有儿童部件。

打开src/index.js:

1nano src/index.js

react-redux包中导入供应商组件. 将供应商添加到您的根组件中,并在任何其他组件周围,对您的代码进行以下突出更改:

 1[label redux-tutorial/src/index.js]
 2import React from 'react';
 3import ReactDOM from 'react-dom';
 4import './index.css';
 5import App from './components/App/App';
 6import * as serviceWorker from './serviceWorker';
 7import { Provider } from 'react-redux';
 8
 9ReactDOM.render(
10  <React.StrictMode>
11    <Provider>
12      <App />
13    </Provider>
14  </React.StrictMode>,
15  document.getElementById('root')
16);
17
18// If you want your app to work offline and load faster, you can change
19// unregister() to register() below. Note this comes with some pitfalls.
20// Learn more about service workers: https://bit.ly/CRA-PWA
21serviceWorker.unregister();

现在你已经包装了你的组件,是时候添加一个商店商店是你的中央数据收集。在下一步,你将学习创建减少器,将设置默认值并更新你的商店,但现在你将硬编码数据。

從「減少」輸入「CreateStore」函數,然後傳送一個函數,返回 object。在這種情況下,返回一個物體,名為「鳥類」的字段,指向個別鳥類的 array

 1[label redux-tutorial/src/index.js]
 2import React from 'react';
 3import ReactDOM from 'react-dom';
 4import './index.css';
 5import App from './components/App/App';
 6import * as serviceWorker from './serviceWorker';
 7import { Provider } from 'react-redux';
 8import { createStore } from 'redux';
 9
10const store = createStore(() => ({
11  birds: [
12    {
13      name: 'robin',
14      views: 1
15    }
16  ]
17}));
18
19ReactDOM.render(
20  <React.StrictMode>
21    <Provider store={store}>
22      <App />
23    </Provider>
24  </React.StrictMode>,
25  document.getElementById('root')
26);
27
28// If you want your app to work offline and load faster, you can change
29// unregister() to register() below. Note this comes with some pitfalls.
30// Learn more about service workers: https://bit.ly/CRA-PWA
31serviceWorker.unregister();

保存并关闭文件. 现在你有一些数据,你需要能够显示它. 打开 src/components/App/App.js:

1nano src/components/App/App.js

背景一样,每个儿童组件将能够访问商店,而无需额外的补贴。 若要访问您的 Redux 商店中的项目,请从反复减少包中使用一个名为useSelectorHook

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useSelector } from 'react-redux';
 4import './App.css';
 5
 6function App() {
 7  const birds = useSelector(state => state.birds);
 8
 9  return <></>
10}
11
12export default App;

由于useSelector是一个自定义的胡克,该组件每次呼叫胡克时都会重新渲染,这意味着数据(鸟类)将始终保持最新状态。

现在你有数据,你可以将其显示在一个未分类的列表中。 创建一个周围的 <div>classNamewrapper. 内部,添加一个 <ul> 元素,并在 birds 阵列上与 map(),返回一个新的 <li> 元素为每一个。

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useSelector } from 'react-redux'
 4import './App.css';
 5
 6function App() {
 7  const birds = useSelector(state => state.birds);
 8
 9  return (
10    <div className="wrapper">
11      <h1>Bird List</h1>
12      <ul>
13        {birds.map(bird => (
14          <li key={bird.name}>
15            <h3>{bird.name}</h3>
16            <div>
17              Views: {bird.views}
18            </div>
19          </li>
20        ))}
21      </ul>
22    </div>
23  );
24}
25
26export default App;

一旦保存了文件,浏览器将重新加载,你会找到你的鸟类列表:

List of birds

现在你有一个基本的列表,添加其余的组件,你需要你的鸟类观看应用程序. 首先,添加一个按钮,以增加视图列表后视图:

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useSelector } from 'react-redux'
 4import './App.css';
 5
 6function App() {
 7  const birds = useSelector(state => state.birds);
 8
 9  return (
10    <div className="wrapper">
11      <h1>Bird List</h1>
12      <ul>
13        {birds.map(bird => (
14          <li key={bird.name}>
15            <h3>{bird.name}</h3>
16            <div>
17              Views: {bird.views}
18              <button><span role="img" aria-label="add"></span></button>
19            </div>
20          </li>
21        ))}
22      </ul>
23    </div>
24  );
25}
26
27export default App;

接下来,在鸟类列表前创建一个<form>和一个单个<input>以便用户可以添加一个新鸟类,请确保包围<input>一个<label>,并在添加按钮中添加提交类型,以确保一切都可访问:

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useSelector } from 'react-redux'
 4import './App.css';
 5
 6function App() {
 7  const birds = useSelector(state => state.birds);
 8
 9  return (
10    <div className="wrapper">
11      <h1>Bird List</h1>
12      <form>
13        <label>
14          <p>
15            Add Bird
16          </p>
17          <input type="text" />
18        </label>
19        <div>
20          <button type="submit">Add</button>
21        </div>
22      </form>
23      <ul>
24        {birds.map(bird => (
25          <li key={bird.name}>
26            <h3>{bird.name}</h3>
27            <div>
28              Views: {bird.views}
29              <button><span role="img" aria-label="add"></span></button>
30            </div>
31          </li>
32        ))}
33      </ul>
34    </div>
35  );
36}
37
38export default App;

保存并关闭文件。接下来,打开App.css,添加一些风格:

1nano src/components/App/App.css

包装类添加一些包装。然后使用h3元素,其中包含鸟的名称。最后,样式按钮。在添加<按钮>上删除默认按钮样式,然后向表单<按钮>添加边缘。

将文件的内容替换为以下内容:

 1[label redux-tutorial/src/components/App/App.css]
 2
 3.wrapper {
 4    padding: 20px;
 5}
 6
 7.wrapper h3 {
 8    text-transform: capitalize;
 9}
10
11.wrapper form button {
12    margin: 10px 0;
13    cursor: pointer;
14}
15
16.wrapper ul button {
17    background: none;
18    border: none;
19    cursor: pointer;
20}

此外,给每个按钮一个指针指针,这将改变指针当在按钮上浮动时,以表明用户按钮是可点击的。

保存和关闭文件. 当您这样做时,浏览器将更新您的组件:

Bird watching app with form

按钮和表单尚未连接到任何操作,因此无法与 Redux 商店互动. 您将在步骤 2 中添加操作,并在步骤 3 中连接它们。

在此步骤中,您安装了 Redux 并为您的应用程序创建了一个新商店. 您使用提供商将商店连接到您的应用程序,并使用useSelector口袋访问了组件中的元素。

在下一步中,您将创建操作和减速器以更新您的商店新信息。

步骤 2 – 创建行动和减轻

接下来,您将创建操作以添加鸟类并增加视图,然后创建一个缩放器,根据操作类型更新信息,最后,您将使用缩放器创建使用combineReducers的默认存储。

操作是您发送给数据存储的信息,随意更改。 缩放器会收到这些信息,并通过应用变更来更新共享存储,取决于操作类型。 您的组件会发送他们希望您的商店使用的操作,而缩放器会使用操作来更新商店中的数据。

在本教程中,您将根据域进行组织,这意味着您的操作和减速器将由它们影响的功能类型定义。

创建一个名为商店的目录:

1mkdir src/store

该目录将包含您的所有操作和减速器。一些模式将它们存储在组件旁边,但这里的优点是您有一个单独的参考点,用于整个商店的形状。

商店目录中创建一个名为鸟类的目录,此目录将包含具体更新您的鸟类数据的操作和减速器:

1mkdir src/store/birds

接下来,打开一个名为birds.js的文件,以便您可以开始添加操作和减速器. 如果您有大量的操作和减速器,您可能希望将它们分成单独的文件,如birds.actions.jsbirds.reducers.js,但当它们只有少数时,它们在相同的位置时更容易阅读:

1nano src/store/birds/birds.js

操作是您使用名为发送的方法从组件发送到您的商店的消息,您将在下一步中使用。

一个操作必须返回一个具有类型字段的对象,否则返回对象可能包含您要发送的任何额外信息。

创建名为addBirds的函数,将作为参数,并将包含ADD_BIRD类型作为字段的对象返回:

1[label redux-tutorial/src/store/birds/birds.js]
2export function addBird(bird) {
3  return {
4    type: 'ADD_BIRD',
5    bird,
6  }
7}

请注意,您正在导出该函数,以便稍后从组件中导入和发送该函数。

类型字段对于与减速器进行通信至关重要,因此大多数Redux商店会将类型保存为变量,以防止写错。

创建一个名为ADD_BIRDconst,保存字符串ADD_BIRD

1[label redux-tutorial/src/store/birds/birds.js]
2const ADD_BIRD = 'ADD_BIRD';
3
4export function addBird(bird) {
5  return {
6    type: ADD_BIRD,
7    bird,
8  }
9}

现在你有行动,创建一个减少器,将响应行动。

减速器是会决定一个状态如何根据行动改变的函数,而行动本身不会做出变化;减速器会采取状态并根据行动做出变化。

减速器收到两个参数:当前状态和行动。当前状态是指商店的特定部分的状态。一般来说,减速器的名称将与商店中的一个字段匹配。

1{
2  birds: [
3    // collection of bird objects
4  ],
5  gear: {
6    // gear information
7  }
8}

您将创建两个减速器:变速器状态减速器将是鸟的数组。

birds.js内部,创建一个名为birds的减速器,它采取状态行动,并返回状态而没有任何变化:

 1[label redux-tutorial/src/store/birds/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3
 4export function addBird(bird) {
 5  return {
 6    type: ADD_BIRD,
 7    bird,
 8  }
 9}
10
11function birds(state, action) {
12  return state;
13}

请注意,您不会直接使用减速器,而是将其合并成可用集合,将其导出并用于在index.js中创建您的基存储。 此外,请注意,如果没有更改,您需要返回状态

最后,由于 Redux 返回状态,如果没有更改,请使用 默认参数添加默认状态。

创建一个DefaultBirds数组,其中将有位置持有者鸟的信息,然后更新状态,以将DefaultBirds作为默认参数:

 1[label redux-tutorial/src/store/birds/birds]
 2const ADD_BIRD = 'ADD_BIRD';
 3
 4export function addBird(bird) {
 5  return {
 6    type: ADD_BIRD,
 7    bird,
 8  }
 9}
10
11const defaultBirds = [
12  {
13    name: 'robin',
14    views: 1,
15  }
16];
17
18function birds(state=defaultBirds, action) {
19  return state;
20}

现在你有一个减速器返回你的状态,你可以使用该操作应用更改. 最常见的模式是使用action.type上的 switch来应用更改。

创建一个交换声明,它将查看action.type。如果该情况是ADD_BIRD,则 spread将当前状态扩展到一个新的数组,并添加一个单一视图的鸟:

 1[label redux-tutorial/src/store/birds/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3
 4export function addBird(bird) {
 5  return {
 6    type: ADD_BIRD,
 7    bird,
 8  }
 9}
10
11const defaultBirds = [
12  {
13    name: 'robin',
14    views: 1,
15  }
16];
17
18function birds(state=defaultBirds, action) {
19  switch (action.type) {
20    case ADD_BIRD:
21      return [
22        ...state,
23        {
24          name: action.bird,
25          views: 1
26        }
27      ];
28    default:
29      return state;
30  }
31}

请注意,您将状态返回为默认值,更重要的是,您不会直接改变状态

现在你有一个操作,你可以创建一个增加视图的操作。

创建名为incrementBird的操作. 与addBird的操作一样,此操作将以鸟为参数,并以类型返回对象。

 1[label redux-tutorial/src/store/birds/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3const INCREMENT_BIRD = 'INCREMENT_BIRD';
 4
 5export function addBird(bird) {
 6  return {
 7    type: ADD_BIRD,
 8    bird,
 9  }
10}
11
12export function incrementBird(bird) {
13  return {
14    type: INCREMENT_BIRD,
15    bird
16  }
17}
18
19const defaultBirds = [
20  {
21    name: 'robin',
22    views: 1,
23  }
24];
25
26function birds(state=defaultBirds, action) {
27  switch (action.type) {
28    case ADD_BIRD:
29      return [
30        ...state,
31        {
32          name: action.bird,
33          views: 1
34        }
35      ];
36    default:
37      return state;
38  }
39}

此操作是单独的,但您将使用相同的减速器. 请记住,这些操作传输您要对数据进行的更改,减速器将这些更改应用到返回新的状态。

增加一只鸟比添加一只新鸟要多一点。在中添加一个新的INCREMENT_BIRD案例。然后将你需要从数组中增加的拉出来,使用 find()来比较每个名称action.bird:

 1[label redux-tutorial/src/store/bird/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3...
 4function birds(state=defaultBirds, action) {
 5  switch (action.type) {
 6    case ADD_BIRD:
 7      return [
 8        ...state,
 9        {
10          name: action.bird,
11          views: 1
12        }
13      ];
14    case INCREMENT_BIRD:
15      const bird = state.find(b => action.bird === b.name);
16      return state;
17    default:
18      return state;
19  }
20}

您有您需要更改的鸟类,但您需要返回一个包含所有未更改的鸟类以及您正在更新的鸟类的新状态。 通过选择所有不等于action.name名称state.filter来选择所有剩余的鸟类。

 1[label redux-tutorial/src/store/bird/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3...
 4
 5function birds(state=defaultBirds, action) {
 6  switch (action.type) {
 7    case ADD_BIRD:
 8      return [
 9        ...state,
10        {
11          name: action.bird,
12          views: 1
13        }
14      ];
15    case INCREMENT_BIRD:
16      const bird = state.find(b => action.bird === b.name);
17      const birds = state.filter(b => action.bird !== b.name);
18      return [
19        ...birds,
20        bird,
21      ];
22    default:
23      return state;
24  }
25}

最后,通过创建一个增加的视图的新对象来更新:

 1[label redux-tutorial/src/store/bird/birds.js]
 2const ADD_BIRD = 'ADD_BIRD';
 3...
 4function birds(state=defaultBirds, action) {
 5  switch (action.type) {
 6    case ADD_BIRD:
 7      return [
 8        ...state,
 9        {
10          name: action.bird,
11          views: 1
12        }
13      ];
14    case INCREMENT_BIRD:
15      const bird = state.find(b => action.bird === b.name);
16      const birds = state.filter(b => action.bird !== b.name);
17      return [
18        ...birds,
19        {
20          ...bird,
21          views: bird.views + 1
22        }
23      ];
24    default:
25      return state;
26  }
27}

请注意,您不使用减排器来排序数据。排序可能被认为是一个视图问题,因为视图向用户显示信息。您可能有一个视图以名称排序和一个视图以视图计数排序,所以最好让单个组件处理排序。

在生产应用程序中,您需要在添加之前验证或给鸟类一个独特的id,以便您可以选择鸟类的id而不是名称

现在你有两个完整的操作和一个减速器。最后一步是导出减速器,以便它可以初始化商店。在第一个步骤中,你通过传递返回对象的函数来创建商店。在这种情况下,你会做同样的事情。该函数将采取商店行动,然后将商店的特定片段传递给减速器。

1export function birdApp(store={}, action) {
2    return {
3        birds: birds(store.birds, action)
4    }
5}

为了简化事情,Redux有一个名为combineReducers的辅助函数,为您组合减速器。

birds.js中,从redux中导入combineReducers

 1[label redux-tutorial/src/store/bird/birds.js]
 2import { combineReducers } from 'redux';
 3const ADD_BIRD = 'ADD_BIRD';
 4const INCREMENT_BIRD = 'INCREMENT_BIRD';
 5
 6export function addBird(bird) {
 7  return {
 8    type: ADD_BIRD,
 9    bird,
10  }
11}
12
13export function incrementBird(bird) {
14  return {
15    type: INCREMENT_BIRD,
16    bird
17  }
18}
19
20const defaultBirds = [
21  {
22    name: 'robin',
23    views: 1,
24  }
25];
26
27function birds(state=defaultBirds, action) {
28  switch (action.type) {
29    case ADD_BIRD:
30      return [
31        ...state,
32        {
33          name: action.bird,
34          views: 1
35        }
36      ];
37    case INCREMENT_BIRD:
38      const bird = state.find(b => action.bird === b.name);
39      const birds = state.filter(b => action.bird !== b.name);
40      return [
41        ...birds,
42        {
43          ...bird,
44          views: bird.views + 1
45        }
46      ];
47    default:
48      return state;
49  }
50}
51
52const birdApp = combineReducers({
53  birds
54});
55
56export default birdApp;

保存并关闭文件。

您的动作和减速器都已设置,最后一步是使用组合减速器来初始化您的商店,而不是设定位置的功能。

打开src/index.js:

1nano src/index.js

birds.js导入birdApp,然后使用birdApp初始化商店:

 1[label redux-tutorial/src/index.js]
 2
 3import React from 'react';
 4import ReactDOM from 'react-dom';
 5import './index.css';
 6import App from './components/App/App';
 7import * as serviceWorker from './serviceWorker';
 8import { Provider } from 'react-redux'
 9import { createStore } from 'redux'
10import birdApp from './store/birds/birds';
11
12const store = createStore(birdApp);
13
14ReactDOM.render(
15  <React.StrictMode>
16    <Provider store={store}>
17      <App />
18    </Provider>
19  </React.StrictMode>,
20  document.getElementById('root')
21);
22
23// If you want your app to work offline and load faster, you can change
24// unregister() to register() below. Note this comes with some pitfalls.
25// Learn more about service workers: https://bit.ly/CRA-PWA
26serviceWorker.unregister();

保存和关闭文件. 当你这样做时,浏览器将更新你的应用程序:

Bird watching app with form

在此步骤中,您创建了操作和减速器,您学会了如何创建返回类型的操作,以及如何构建使用该操作构建和返回基于该操作的新状态的减速器。

您的 Redux 商店现在已全部设置并为更改做好准备. 在下一步中,您将从组件发送操作以更新数据。

步骤 3 – 在组件中发送更改

在此步骤中,您将从您的组件导入并调用您的操作. 您将使用名为发送的方法发送该操作,并将以表单按钮发送事件处理器(https://andsky.com/tech/tutorials/how-to-handle-dom-and-window-events-with-react)内部的操作。

到此步骤结束时,您将有一个工作应用程序,该应用程序将 Redux 商店和您的自定义组件相结合。

现在你有工作操作,你需要将它们连接到你的事件,以便你可以更新商店。你将使用的方法称为发送,它将发送一个特定的操作到Redux商店。

打开App.js:

1nano src/components/App/App.js

App.js内,从react-redux中导入useDispath头条,然后调用该函数来创建一个新的发送函数:

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useDispatch, useSelector } from 'react-redux'
 4import './App.css';
 5
 6function App() {
 7  ...
 8}
 9
10export default App;

接下来,您需要导入您的操作,请记住,操作是返回对象的函数,对象是您最终将传入发送函数的内容。

从商店中导入incrementBird。然后在按钮上创建一个onClick事件。当用户点击按钮时,用bird.name打电话给incrementBird,然后将结果传输到dispatch

 1[label redux-tutorial/src/components/App/App.js]
 2import React from 'react';
 3import { useDispatch, useSelector } from 'react-redux'
 4import { incrementBird } from '../../store/birds/birds';
 5import './App.css';
 6
 7function App() {
 8  const birds = useSelector(state => state.birds);
 9  const dispatch = useDispatch();
10
11  return (
12    <div className="wrapper">
13      <h1>Bird List</h1>
14      <form>
15        <label>
16          <p>
17            Add Bird
18          </p>
19          <input type="text" />
20        </label>
21        <div>
22          <button type="submit">Add</button>
23        </div>
24      </form>
25      <ul>
26        {birds.map(bird => (
27          <li key={bird.name}>
28            <h3>{bird.name}</h3>
29            <div>
30              Views: {bird.views}
31              <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add"></span></button>
32            </div>
33          </li>
34        ))}
35      </ul>
36    </div>
37  );
38}
39
40export default App;

保存文件. 当你这样做时,你将能够增加罗宾数:

Increment a bird

接下来,您需要发送addBird操作,这需要两个步骤:将输入保存到内部状态,并使用onSubmit启动发送。

使用 useState Hook 来保存输入值. 请确保将输入转换为受控制的组件,通过在输入中设置 `value. 请参阅教程 How To Build Forms in React 以深入了解受控制的组件。

对您的代码进行以下更改:

 1[label redux-tutorial/src/components/App/App.js]
 2import React, { useState } from 'react';
 3import { useDispatch, useSelector } from 'react-redux'
 4import { incrementBird } from '../../store/birds/birds';
 5import './App.css';
 6
 7function App() {
 8  const [birdName, setBird] = useState('');
 9  const birds = useSelector(state => state.birds);
10  const dispatch = useDispatch();
11
12  return (
13    <div className="wrapper">
14      <h1>Bird List</h1>
15      <form>
16        <label>
17          <p>
18            Add Bird
19          </p>
20          <input
21            type="text"
22            onChange={e => setBird(e.target.value)}
23            value={birdName}
24          />
25        </label>
26        <div>
27          <button type="submit">Add</button>
28        </div>
29      </form>
30      <ul>
31        ...
32      </ul>
33    </div>
34  );
35}
36
37export default App;

接下来,从birds.js导入addBird,然后创建一个名为handleSubmit的函数。在handleSubmit函数内,以event.preventDefault阻止页面表单提交,然后将addBird操作发送到birdName作为参数。发送该操作后,拨打setBird()`来清除输入。

 1[label redux-tutorial/src/components/App/App.js]
 2import React, { useState } from 'react';
 3import { useDispatch, useSelector } from 'react-redux'
 4import { addBird, incrementBird } from '../../store/birds/birds';
 5import './App.css';
 6
 7function App() {
 8  const [birdName, setBird] = useState('');
 9  const birds = useSelector(state => state.birds);
10  const dispatch = useDispatch();
11
12  const handleSubmit = event => {
13    event.preventDefault();
14    dispatch(addBird(birdName))
15    setBird('');
16  };
17
18  return (
19    <div className="wrapper">
20      <h1>Bird List</h1>
21      <form onSubmit={handleSubmit}>
22        <label>
23          <p>
24            Add Bird
25          </p>
26          <input
27            type="text"
28            onChange={e => setBird(e.target.value)}
29            value={birdName}
30          />
31        </label>
32        <div>
33          <button type="submit">Add</button>
34        </div>
35      </form>
36      <ul>
37        {birds.map(bird => (
38          <li key={bird.name}>
39            <h3>{bird.name}</h3>
40            <div>
41              Views: {bird.views}
42              <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add"></span></button>
43            </div>
44          </li>
45        ))}
46      </ul>
47    </div>
48  );
49}
50
51export default App;

当你这样做时,浏览器将重新加载,你将能够添加一个鸟:

Save new bird

您现在正在调用您的操作并更新您的鸟类列表在商店中。 请注意,当您的应用程序更新时,您丢失了以前的信息。

如果在列表中增加一只鸟,此列表的顺序也会发生变化。

Robin goes to the bottom on reorder

正如您在第 2 步中所看到的,您的 Reducer 并不关心对数据的分类。 为了防止组件发生意外的变化,您可以将组件中的数据分类。 将一个 sort() 函数添加到 birds 数组中。 请记住,分类会改变数组,而您绝不会想要改变存储。 请确保在分类前通过散布数据来创建一个新的数组:

 1[label redux-tutorial/src/components/App/App.js]
 2import React, { useState } from 'react';
 3import { useDispatch, useSelector } from 'react-redux'
 4import { addBird, incrementBird } from '../../store/birds/birds';
 5import './App.css';
 6
 7function App() {
 8  const [birdName, setBird] = useState('');
 9  const birds = [...useSelector(state => state.birds)].sort((a, b) => {
10    return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1;
11  });
12  const dispatch = useDispatch();
13
14  const handleSubmit = event => {
15    event.preventDefault();
16    dispatch(addBird(birdName))
17    setBird('');
18  };
19
20  return (
21    <div className="wrapper">
22      <h1>Bird List</h1>
23      <form onSubmit={handleSubmit}>
24        <label>
25          <p>
26            Add Bird
27          </p>
28          <input
29            type="text"
30            onChange={e => setBird(e.target.value)}
31            value={birdName}
32          />
33        </label>
34        <div>
35          <button type="submit">Add</button>
36        </div>
37      </form>
38      <ul>
39        {birds.map(bird => (
40          <li key={bird.name}>
41            <h3>{bird.name}</h3>
42            <div>
43              Views: {bird.views}
44              <button onClick={() => dispatch(incrementBird(bird.name))}><span role="img" aria-label="add"></span></button>
45            </div>
46          </li>
47        ))}
48      </ul>
49    </div>
50  );
51}
52
53export default App;

当你这样做时,组件将保持在字母顺序,当你增加鸟类。

Cardinal stays on top

重要的是不要在您的 Redux 商店中尝试和做太多,让减速器专注于保持最新信息,然后为您的用户在组件中拉动和操纵数据。

<$>[注] 注: 在本教程中,请注意,每个操作和减速器都有一个公平的代码量。幸运的是,有一个官方支持的项目叫做 Redux Toolkit,可以帮助您减少锅炉板代码的数量。

在此步骤中,您从组件发送了您的操作,您学会了如何调用操作,以及如何将结果发送到发送函数,并将它们连接到您的组件上的事件处理器,以创建一个完全交互式的商店。

结论

Redux 是一个流行的单一商店。在使用需要共享信息源的组件时,它可能是有益的,但在所有项目中,它并不总是正确的选择。较小的项目或具有孤立组件的项目将能够使用内置的状态管理和背景。但是,随着应用程序的复杂性增加,您可能会发现中央存储对于维护数据完整性至关重要。

如果您想阅读更多 React 教程,请查看我们的 React 主题页面,或返回 如何在 React.js 系列中编码页面

Published At
Categories with 技术
comments powered by Disqus