作者选择了 Creative Commons以作为 Write for Donations计划的一部分获得捐赠。
介绍
Redux是用于 JavaScript和 React应用的流行的数据存储,它遵循一个核心原则,即数据绑定应该以一个方向流动,并应作为一个单一的真理源存储。
Redux 根据几个概念运作. 首先, store 是一个单个对象,每个数据选择都有字段. 您通过发送一个 action 来更新数据,说明数据应该如何改变。
在小型应用程序中,您可能不需要一个全球数据存储库. 您可以使用 本地状态和 背景的组合来管理状态。
在本教程中,您将通过构建鸟类观察测试应用程序来在 React 应用程序中使用 Redux。用户将能够添加他们已经看到的鸟类,并在每次再次看到它时增加一只鸟类。
前提条件
- 联合国 您需要一个运行 [Node.js] (https://nodejs.org/en/about/) 的开发环境; 此教程在 Node.js 版本 10.22.0 和 npm 版本 6.14.6. 上进行了测试 。 要在 macOS 或 Ubuntu 18.04 上安装此功能,请遵循 [如何在 macOS (https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos 上安装节点并创建本地开发环境] 或 [如何在 Ubuntu 18.04 (https://andsky.com/tech/tutorials/how-to-install-node-js-on-ubuntu-18-04 上安装节点.js] 的 ** 部分使用 PPA** 。
- 用Create React App来设置反应开发环境,将非必需锅炉板取出. 要设置此功能, 请遵循 [步骤1—— 创建一个空项目, 如何管理响应类组件的状态教程] (https://andsky.com/tech/tutorials/how-to-manage-state-on-react-class-components#step-1-%E2%80%94-creating-an-empty-project) 。 这个教程将使用
reux-tutorial
作为项目名称。 - 您将在此教程中使用 React 组件、 Hooks 和表单, 包括
使用状态
虎克和定制虎克. 您可以在我们的教程中学习组件和钩子如何在反应组件上用钩子管理状态和如何在反应中构建表格。 - 您还需要JavaScript, HTML, CSS 的基本知识, 您可以在 [How To Build a website With HTML series] (https://www.digitalocean.com/community/tutorial_series/how-to-build-a-website-with-html), [How To Build a website With CSS series] (https://www.digitalocean.com/community/tutorial_series/how-to-build-a-website-with-css) 和 [How To Code in JavaScript] (https://www.digitalocean.com/community/tutorial_series/how-to-code-in-javascript)中找到这些知识. (英语)
步骤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 商店中的项目,请从反复减少
包中使用一个名为useSelector
的 Hook。
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>
与 className
的 wrapper
. 内部,添加一个 <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;
一旦保存了文件,浏览器将重新加载,你会找到你的鸟类列表:
现在你有一个基本的列表,添加其余的组件,你需要你的鸟类观看应用程序. 首先,添加一个按钮,以增加视图列表后视图:
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}
此外,给每个按钮一个指针
的指针
,这将改变指针当在按钮上浮动时,以表明用户按钮是可点击的。
保存和关闭文件. 当您这样做时,浏览器将更新您的组件:
按钮和表单尚未连接到任何操作,因此无法与 Redux 商店互动. 您将在步骤 2 中添加操作,并在步骤 3 中连接它们。
在此步骤中,您安装了 Redux 并为您的应用程序创建了一个新商店. 您使用提供商
将商店连接到您的应用程序,并使用useSelector
口袋访问了组件中的元素。
在下一步中,您将创建操作和减速器以更新您的商店新信息。
步骤 2 – 创建行动和减轻
接下来,您将创建操作以添加鸟类并增加视图,然后创建一个缩放器,根据操作类型更新信息,最后,您将使用缩放器创建使用combineReducers
的默认存储。
操作是您发送给数据存储的信息,随意更改。 缩放器会收到这些信息,并通过应用变更来更新共享存储,取决于操作类型。 您的组件会发送他们希望您的商店使用的操作,而缩放器会使用操作来更新商店中的数据。
在本教程中,您将根据域进行组织,这意味着您的操作和减速器将由它们影响的功能类型定义。
创建一个名为商店
的目录:
1mkdir src/store
该目录将包含您的所有操作和减速器。一些模式将它们存储在组件旁边,但这里的优点是您有一个单独的参考点,用于整个商店的形状。
在商店
目录中创建一个名为鸟类
的目录,此目录将包含具体更新您的鸟类数据的操作和减速器:
1mkdir src/store/birds
接下来,打开一个名为birds.js
的文件,以便您可以开始添加操作和减速器. 如果您有大量的操作和减速器,您可能希望将它们分成单独的文件,如birds.actions.js
和birds.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_BIRD
的const
,保存字符串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();
保存和关闭文件. 当你这样做时,浏览器将更新你的应用程序:
在此步骤中,您创建了操作和减速器,您学会了如何创建返回类型
的操作,以及如何构建使用该操作构建和返回基于该操作的新状态的减速器。
您的 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;
保存文件. 当你这样做时,你将能够增加罗宾数:
接下来,您需要发送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;
当你这样做时,浏览器将重新加载,你将能够添加一个鸟:
您现在正在调用您的操作并更新您的鸟类列表在商店中。 请注意,当您的应用程序更新时,您丢失了以前的信息。
如果在列表中增加一只鸟,此列表的顺序也会发生变化。
正如您在第 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;
当你这样做时,组件将保持在字母顺序,当你增加鸟类。
重要的是不要在您的 Redux 商店中尝试和做太多,让减速器专注于保持最新信息,然后为您的用户在组件中拉动和操纵数据。
<$>[注] 注: 在本教程中,请注意,每个操作和减速器都有一个公平的代码量。幸运的是,有一个官方支持的项目叫做 Redux Toolkit,可以帮助您减少锅炉板代码的数量。
在此步骤中,您从组件发送了您的操作,您学会了如何调用操作,以及如何将结果发送到发送函数,并将它们连接到您的组件上的事件处理器,以创建一个完全交互式的商店。
结论
Redux 是一个流行的单一商店。在使用需要共享信息源的组件时,它可能是有益的,但在所有项目中,它并不总是正确的选择。较小的项目或具有孤立组件的项目将能够使用内置的状态管理和背景。但是,随着应用程序的复杂性增加,您可能会发现中央存储对于维护数据完整性至关重要。
如果您想阅读更多 React 教程,请查看我们的 React 主题页面,或返回 如何在 React.js 系列中编码页面。