React - redux 使用(由浅入深)
- 一. redux理解
- 1. redux 介绍
- 2. redux 使用情况
- 3. redux 工作流程
- 4. redux 三个核心概念
- 4.1 Action
- 4.2 Store
- 4.3 Reducers
- 5. redux 核心API
- 5.1 createStore()
- 5.2 Store
- 5.2.1 Store 方法
- 5.2.1.1 getState()
- 5.2.1.2 dispatch(action)
- 5.2.1.3 subscribe(listener)
- 二. redux精简版使用(不使用 Action)
- 三. redux完整版使用(在 redux精简版基础上使用 Action)
- 四. redux 异步action(在 redux完整版基础上使用 异步action)
- 五. react-redux 的基本使用(在 redux 异步action修改使用 react-redux)
- 六. react-redux 的优化使用(在 react-redux 的基本使用 中修改)
- 七. react-redux 数据共享(在 react-redux 的优化使用 中修改)
- 八. react-redux 总体优化(在 react-redux 数据共享 中修改)
- 九. react-redux 开发者工具的使用
- 十. react-redux 正规使用流程
- 1. 准备工作,创建 redux 所需文件
- 2. 安装 redux 所需的依赖,编写基础代码
- 3. 编写组件功能
- 3.1 Count 组件 实例
- 3.2 Persons 组件 实例
- 3.3 Count组件 和 Persons 组件 相互通信
- 3.4 页面展示
中文文档:http://www.redux.org.cn/
英文文档:https://redux.js.org/
Github:https://github.com/reactjs/redux
可直接参照 目录十 进行使用 react-redux
一. redux理解
1. redux 介绍
- redux 是一个专门用于做状态管理的JS库(不是react插件库)。
- 它可以用在 react, angular, vue 等项目中, 但基本与 react 配合使用。
- 作用: 集中式管理 react 应用中多个组件共享的状态。
- 可以构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。
- 体小精悍(只有2kB,包括依赖),却有很强大的插件扩展生态。
2. redux 使用情况
- 一个状态多个组件使用,多个组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 能不使用就不使用, 状态共享使用不方便时考虑使用。
3. redux 工作流程
4. redux 三个核心概念
4.1 Action
- 可以省略创建。
- 把数据从应用传到 store 的有效载荷,Action 是 store 数据的唯一来源。
- 一般来说会通过
store.dispatch()
将 action 传到 store。 - 有两个属性
type
:标识属性, 值为字符串, 唯一, 必要属性。
data
:数据属性, 值类型任意, 可选属性
4.2 Store
- 用来维持应用所有的 state 树 的一个对象,改变 store 内 state 的惟一途径是对它 dispatch 一个 action。
- Store 不是类,它只是有几个方法的对象。 要创建它,只需要把根部的 reducing 函数传递给 createStore。
4.3 Reducers
- 指定了应用状态的变化如何响应 actions 并发送到 store 。
- Reduce r函数会接收到两个参数,分别为:
之前的状态
、动作对象
。 - Reducer 有两个作用:初始化状态、加工状态。
- Reducer 的第一次调用时,是store自动触发的,传递的
preState
(之前的状态) 是undefined
5. redux 核心API
5.1 createStore()
- 创建一个 Redux store 来以存放应用中所有的 state。
- 应用中应有且仅有一个 store。
5.2 Store
- 用来维持应用所有的 state 树 的一个对象。 改变 store 内 state 的惟一途径是对它 dispatch 一个 action。
- Store 不是类。它只是有几个方法的对象。 要创建它,只需要把根部的 reducing 函数传递给 createStore。
5.2.1 Store 方法
5.2.1.1 getState()
store.getState()
- 返回应用当前的 state 树。
- 它与 store 的最后一个 reducer 返回值相同。
5.2.1.2 dispatch(action)
store.dispatch(action)
- 分发 action。这是触发 state 变化的惟一途径。
- 会使用当前 getState() 的结果和传入的 action 以同步方式的调用 store 的 reduce 函数。返回值会被作为下一个 state。
5.2.1.3 subscribe(listener)
store.subscribe(() => { });
- 添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。可以在回调函数里调用 getState() 来拿到当前 state。
- 状态改变后重新渲染,有两种方法:
(1)在组件的 componentDidMount 生命周期钩子中调用store.subscribe
(2)在 index.js 文件中检测 store 中状态的改变,一旦发生改变重新渲染componentDidMount() { // 监听redux中状态的变化,只要变化,就调用render store.subscribe(() => { //状态假更新,每次状态更新都会调用render this.setState({}); }); }
<App/>
import React from "react"; import reactDOM from "react-dom"; import App from "./App"; import store from "./redux/store"; reactDOM.render(<App />, document.getElementById("root")); // store中状态改变,重新渲染dom store.subscribe(() => { reactDOM.render(<App />, document.getElementById("root")); });
二. redux精简版使用(不使用 Action)
- src 文件夹下创建一个
redux
文件夹,并在 redux 文件夹中创建两个文件,分别是:store.js
、count_reducer.js
,对应store,reducer - store.js中修改
1.引入redux中的createState函数,创建一个store
2. createState调用时,要传入一个为其服务的reducer
3. 记得暴露 store/** * 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ // 引入createStore,专门用于创建redux中最为核心的store对象 import { createStore } from "redux"; // 引入为count组件服务的reducer import countReducer from "./count_reducer"; const store = createStore(countReducer); export default store;
- count_reducer.js中修改
/** * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数 * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action) */ const intState = 0; //初始化状态 // preState===undefined时,preState = intState export default function countReducer(preState = intState, action) { // 从action对象中获取:type,data const { type, data } = action; // 根据type决定加工数据 switch (type) { // 加 case "increment": return preState + data; // 减 case "decrement": return preState - data; default: return preState; } }
- 组件中使用
- 获取状态:store.getState()
- 改变状态:store.dispatch({ type: “方法类型”, data: 需要操作的数据 })
- 状态改变后重新渲染
import React, { Component } from "react"; // 引入store,用于获取redux中保存的状态 import store from "../../redux/store"; export default class Count extends Component { /** * 在index.js中监听状态改变进行渲染,一劳永逸 */ // componentDidMount() { // // 监听redux中状态的变化,只要变化,就调用render // store.subscribe(() => { // // 假更新 // this.setState({}); // }); // } // 加法 increment = () => { const { value } = this.selectNumber; store.dispatch({ type: "increment", data: value * 1 }); }; // 减法 decrement = () => { const { value } = this.selectNumber; store.dispatch({ type: "decrement", data: value * 1 }); }; // 奇数再加 incrementIfOdd = () => { const { value } = this.selectNumber; const count = store.getState(); if (count % 2 !== 0) { store.dispatch({ type: "increment", data: value * 1 }); } }; // 异步加 incrementAsync = () => { const { value } = this.selectNumber; setTimeout(() => { store.dispatch({ type: "increment", data: value * 1 }); }, 500); }; render() { return ( <div> <h1>当前求和为:{store.getState()}</h1> <select ref={(cur) => (this.selectNumber = cur)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ); } }
三. redux完整版使用(在 redux精简版基础上使用 Action)
- 在redux文件夹中新增两个文件,分别是:
constant.js
、count__action.js
constant.js 放置容易写错的type值
count__action.js 专门用于创建action对象 - constant.js 文件修改
/** * 该模块用于定义action对象中type类型的常量值 * 目的只有一个:便于管理的同时防止程序员单词写错 */ export const INCREMENT = "increment"; export const DECREMENT = "decrement";
- count__action.js 文件修改
/** * 该文件专门为Count组件生成action对象 */ import { INCREMENT, DECREMENT } from "./constant"; // 正常方式 // export const createIncrementAction = (data) => { // return { type: "increment", data }; // }; // 简写方式 export const createIncrementAction = (data) => ({ type: INCREMENT, data }); export const createDecrementAction = (data) => ({ type: DECREMENT, data });
- count_reducer.js文件修改,使用 constant.js 文件
/** * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数 * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action) */ import { INCREMENT, DECREMENT } from "./constant"; const intState = 0; //初始化状态 export default function countReducer(preState = intState, action) { // 从action对象中获取:type,data const { type, data } = action; // 根据type决定加工数据 switch (type) { // 加 case INCREMENT: return preState + data; // 减 case DECREMENT: return preState - data; default: return preState; } }
- 使用的组件中修改
import React, { Component } from "react"; // 引入store,用于获取redux中保存的状态 import store from "../../redux/store"; // 引入actionCreator,专门用于创建action对象 import { createIncrementAction, createDecrementAction, } from "../../redux/count_action"; export default class Count extends Component { // 加法 increment = () => { const { value } = this.selectNumber; store.dispatch(createIncrementAction(value * 1)); }; // 减法 decrement = () => { const { value } = this.selectNumber; store.dispatch(createDecrementAction(value * 1)); }; // 奇数再加 incrementIfOdd = () => { const { value } = this.selectNumber; const count = store.getState(); if (count % 2 !== 0) { store.dispatch(createIncrementAction(value * 1)); } }; // 异步加 incrementAsync = () => { const { value } = this.selectNumber; setTimeout(() => { store.dispatch(createIncrementAction(value * 1)); }, 500); }; render() { return ( <div> <h1>当前求和为:{store.getState()}</h1> <select ref={(cur) => (this.selectNumber = cur)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ); } }
四. redux 异步action(在 redux完整版基础上使用 异步action)
- 安装
redux-thunk
yarn add redux-thunk
- 在 store.js 中修改配置
redux-thunk
/** * 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ // 引入createStore,专门用于创建redux中最为核心的store对象 import { createStore, applyMiddleware } from "redux"; // 引入为count组件服务的reducer import countReducer from "./count_reducer"; // 引入redux-thunk,用于支持异步action import thunk from "redux-thunk"; const store = createStore(countReducer, applyMiddleware(thunk)); export default store;
- count_action.js 文件中配置
异步action
/** * 该文件专门为Count组件生成action对象 */ import { INCREMENT, DECREMENT } from "./constant"; /** * 同步action,就是指action的值为Object类型的一般对象 */ // 正常方式 // export const createIncrementAction = (data) => { // return { type: "increment", data }; // }; // 简写方式 export const createIncrementAction = (data) => ({ type: INCREMENT, data }); export const createDecrementAction = (data) => ({ type: DECREMENT, data }); /** * 异步action,就是指action的值是函数,异步action中一般都会调用同步action,异步action不是必须要用的 */ export const createIncrementAsyncAction = (data, time) => { return (dispatch) => { setTimeout(() => { // 异步任务有结果后,分发一个同步的action去真正操作数据 dispatch(createIncrementAction(data)); }, time); }; };
- 组件中修改使用
异步action
// 引入actionCreator,专门用于创建action对象 import { createIncrementAction, createDecrementAction, createIncrementAsyncAction, } from "../../redux/count_action"; // 异步加 incrementAsync = () => { const { value } = this.selectNumber; // setTimeout(() => { store.dispatch(createIncrementAsyncAction(value * 1, 500)); // }, 500); };
五. react-redux 的基本使用(在 redux 异步action修改使用 react-redux)
- 安装 react-redux
yarn add react-redux
- 创建一个容器组件
1.在 src 文件夹下创建一个
containers
容器文件夹
2.containers 文件夹中创建一个Count
文件夹
3.Count 文件夹下创建一个index.jsx
文件// 引入Count的UI组件 import CountUI from "../../components/Count"; // 引入connect用于连接UI组件与redux import { connect } from "react-redux"; // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction, } from "../../redux/count_action"; /** * 1. mapStateToProps 函数的返回值是一个对象 * 2. 返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件的props的value * 3. mapStateToProps 函数用于传递状态 */ function mapStateToProps(state) { return { count: state, }; } /** * 1. mapDispatchToProps 函数的返回值是一个对象 * 2. 返回的对象中的key作为传递给UI组件props的key,value作为传递给UI组件的props的value * 3. mapDispatchToProps 函数用于传递操作状态的方法 */ function mapDispatchToProps(dispatch) { return { add: (data) => { // 通知redux执行方法 dispatch(createIncrementAction(data)); }, delete: (data) => dispatch(createDecrementAction(data)), addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)), }; } // 使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
- App.js 文件中修改引入的容器组件地址,并传入 store
import React, { Component } from "react"; import Count from "./containers/Count"; import store from "./redux/store"; export default class App extends Component { render() { return ( <div> {/* 给容器组件传递store */} <Count store={store} /> </div> ); } }
- 修改组件中获取状态和修改状态的方式,通过
this.props
方式- 获取状态:
this.props.count
- 调用 action 修改状态,对应
mapDispatchToProps
中的方法
this.props.add(value * 1);
- 获取状态:
六. react-redux 的优化使用(在 react-redux 的基本使用 中修改)
-
容器组件 和 UI组件 合并成一个组件
connect
模板:export default connect( (state) => ({ key: value }),//映射状态 { key: action方法, }//映射操作状态的方法 )(UI组件);
1.将 src/component/Count/index.jsx 中的内容 剪切放入 src/container/Count/index.jsx 中
2.并删除 src/component 中的Count组件
3.优化connect()()
写法,简化mapDispatchToProps
写法import React, { Component } from "react"; // 引入connect用于连接UI组件与redux import { connect } from "react-redux"; // 引入action import { createIncrementAction, createDecrementAction, createIncrementAsyncAction, } from "../../redux/count_action"; class Count extends Component { // 加法 increment = () => { const { value } = this.selectNumber; this.props.add(value * 1); }; // 减法 decrement = () => { const { value } = this.selectNumber; this.props.delete(value * 1); }; // 奇数再加 incrementIfOdd = () => { const { value } = this.selectNumber; if (this.props.count % 2 !== 0) { this.props.add(value * 1); } }; // 异步加 incrementAsync = () => { const { value } = this.selectNumber; this.props.addAsync(value * 1,500); }; render() { console.log("UI组件接收到的props值:", this.props); return ( <div> <h1>当前求和为:{this.props.count}</h1> <select ref={(cur) => (this.selectNumber = cur)}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>若当前求和为奇数,再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ); } } /** * 拆分函数写法 // 映射状态 const mapStateToProps = (state) => ({ count: state }); // 映射操作状态的方法 const mapDispatchToProps = (dispatch) => ({ add: (data) => dispatch(createIncrementAction(data)), delete: (data) => dispatch(createDecrementAction(data)), addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)), }); // 使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(Count); */ /** * 合并函数写法 */ // 使用connect()()创建并暴露一个Count的容器组件 export default connect( (state) => ({ count: state }), // mapDispatchToProps 的一般写法 // (dispatch) => ({ // add: (data) => dispatch(createIncrementAction(data)), // delete: (data) => dispatch(createDecrementAction(data)), // addAsync: (data, time) => dispatch(createIncrementAsyncAction(data, time)), // }) // mapDispatchToProps 的简写 { add: createIncrementAction, delete: createDecrementAction, addAsync: createIncrementAsyncAction, } )(Count);
-
修改 App.jsx,无需自己给容器组件传递store
import React, { Component } from "react"; import Count from "./containers/Count"; export default class App extends Component { render() { return ( <div> <Count/> </div> ); } }
-
修改 index.js 文件
1.去除
store.subscribe(() => { });
状态监听
2.给<App/>
包裹一个<Provider store={store}></Provider>
标签
Provider
:让所有组件都可以得到state数据import React from "react"; import reactDOM from "react-dom"; import App from "./App"; import store from "./redux/store"; import { Provider } from "react-redux"; reactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") );
七. react-redux 数据共享(在 react-redux 的优化使用 中修改)
- 修改 src/redux/constant.js 文件,新增常量值
export const INCREMENT = "increment"; export const DECREMENT = "decrement"; export const ADD_PERSON = "add_person";
- src/redux 文件夹中创建一个
actions
文件夹,将所有的 action文件 放入。在其中创建一个person
的 action文件,用于为 person组件 的 reducer服务。import { ADD_PERSON } from "../constant"; // 创建增加一个人的action对象 export const creactAddPersonAction = (personObj) => { return { type: ADD_PERSON, data: personObj }; };
- src/redux 文件夹中创建一个
reducers
文件夹,将所有的 reducers文件 放入。在其中创建一个person
的reducers文件import { ADD_PERSON } from "../constant"; // 初始化人员列表 const initState = [{ id: 1, name: "tom", age: 20 }]; export default function personReducer(preState = initState, action) { console.log('person') const { type, data } = action; switch (type) { // 添加 case ADD_PERSON: return [data, ...preState]; default: return preState; } }
- 修改 store.js 文件,引入
combineReducers
,用于汇总所有的reducer,变为一个整的reducer/** * 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ // 引入createStore,专门用于创建redux中最为核心的store对象 import { createStore, applyMiddleware,combineReducers } from "redux"; // 引入为count组件服务的reducer import countReducer from "./reducers/count"; // 引入Person组件服务的reducer import personReducer from './reducers/person' // 引入redux-thunk,用于支持异步action import thunk from "redux-thunk"; // 汇总所有的reducer,变为一个整的reducer const allReducer = combineReducers({ total:countReducer, persons:personReducer }) const store = createStore(allReducer, applyMiddleware(thunk)); export default store;
- 新增一个 Person 组件,和Count组件通过 redux 共享数据
取出 redux 状态时,要取到对象组件的key值
import React, { Component } from "react"; import { connect } from "react-redux"; import { creactAddPersonAction } from "../../redux/actions/person"; class Person extends Component { addPerson = () => { const name = this.nameNode.value; const age = this.ageNode.value; const id = this.props.personList.length + 1; let personObj = { id, name, age, }; this.props.addPerson(personObj); this.nameNode.value = ""; this.ageNode.value = ""; }; render() { return ( <div> <h2>Person 组件</h2> <h4>上方Count组件的总和为:{this.props.total}</h4> <input ref={(cur) => (this.nameNode = cur)} type="text" placeholder="请输入姓名" /> <input ref={(cur) => (this.ageNode = cur)} type="text" placeholder="请输入年龄" /> <button onClick={this.addPerson}>添加</button> <ul> {this.props.personList.map((v) => { return ( <li key={v.id}> 姓名:{v.name},年龄:{v.age} </li> ); })} </ul> </div> ); } } const mapStateToProps = (state) => ({ personList: state.persons, total: state.total, }); const mapDispatchToProps = { addPerson: creactAddPersonAction, }; export default connect(mapStateToProps, mapDispatchToProps)(Person);
八. react-redux 总体优化(在 react-redux 数据共享 中修改)
- 所有的变量名尽可能规范,并触发对象的简写方式
- 汇总
reducer
时
// 引入combineReducers,用于汇总所有的reducer import { combineReducers } from "redux"; // 引入为count组件服务的reducer import count from "./count"; // 引入Person组件服务的reducer import persons from "./person"; // 汇总所有的reducer,变为一个整的reducer const allReducer = combineReducers({ count, persons, }); export default allReducer;
- 组件中使用 action 方法时
import { addPerson } from "../../redux/actions/person"; const mapStateToProps = (state) => ({ personList: state.persons, total: state.count, }); const mapDispatchToProps = { addPerson, }; export default connect(mapStateToProps, mapDispatchToProps)(Person);
- 汇总
- reducer文件夹中,新增一个index.js文件,专门用于汇总并暴露所有的reducer
store.js 文件中使用/** * 该文件用于汇总所有的reducer为一个总的reducer */ // 引入combineReducers,用于汇总所有的reducer import { combineReducers } from "redux"; // 引入为count组件服务的reducer import count from "./count"; // 引入Person组件服务的reducer import persons from "./person"; // 汇总所有的reducer,变为一个整的reducer const allReducer = combineReducers({ count, persons, }); export default allReducer;
// 引入汇总之后的reducer import allReducer from "./reducers"; const store = createStore( allReducer, applyMiddleware(thunk) ); export default store;
九. react-redux 开发者工具的使用
-
浏览器拓展程序引入
Redux DevTools
-
项目中安装
redux-devtools-extension
yarn add redux-devtools-extension
-
配置store.js文件
// 引入 redux-devtools-extension import { composeWithDevTools } from "redux-devtools-extension"; const store = createStore( allReducer, composeWithDevTools(applyMiddleware(thunk)) );
十. react-redux 正规使用流程
1. 准备工作,创建 redux 所需文件
src 文件夹下创建一个
redux
文件夹
redux 文件夹下创建actions
文件夹、reducers
文件夹、constans.js
文件、store.js
文件
reducers 文件夹下创建index.js
文件
redux 文件层级:
- src
- redux
- actions
- reducers
- index.js
- constans.js
- store.js
actions 文件夹
:
用于为组件生成 action 对象。reducers 文件夹
:
(1)用于创建一个组件服务的 reducer,reducer 的本质是一个函数。
(2)reducer 函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action)。reducers/index.js 文件
:
该文件用于汇总所有的 reducer 为一个总的 reducer 。constans.js 文件
:
(1)用于定义 action 对象中 type 类型的常量值。
(2)目的只有一个:便于管理的同时防止程序员单词写错。store.js 文件
:
该文件专门用于暴露一个 store 对象,整个应用只有一个store 对象。
2. 安装 redux 所需的依赖,编写基础代码
- 安装 redux
用于创建redux中最为核心的store对象
yarn add redux react-redux
- 安装 redux-thunk
用于支持异步 action
yarn add redux-thunk
- 安装 redux-devtools-extension
用于支持 react-redux 开发者工具的使用
yarn add redux-devtools-extension
- src/redux/reducers/index.js 文件修改
// 引入combineReducers,用于汇总所有的reducer import { combineReducers } from "redux"; // 汇总所有的reducer,变为一个整的reducer const allReducer = combineReducers({ }); export default allReducer;
- src/redux/store.js 文件修改
// 引入createStore,专门用于创建redux中最为核心的store对象 import { createStore, applyMiddleware } from "redux"; // 引入redux-thunk,用于支持异步action import thunk from "redux-thunk"; // 引入 redux-devtools-extension import { composeWithDevTools } from "redux-devtools-extension"; // 引入汇总之后的reducer import allReducer from "./reducers"; const store = createStore( allReducer, composeWithDevTools(applyMiddleware(thunk)) ); export default store;
- src/index.js 文件修改
import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; import { Provider } from "react-redux"; import store from "./redux/store"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> {/* 此处需要用 Provider 包裹 App,目的是让 App 所有的后代容器组件都能接收到store */} {/* 可代替下方 store.subscribe(),实现自动监听 */} <Provider store={store}> <App /> </Provider> </React.StrictMode> ); // 监测redux中状态的改变, 若redux中的状态发生了改变,则重新渲染App组件 // store.subscribe(() => { // root.render( // <React.StrictMode> // <App /> // </React.StrictMode> // ); // }); reportWebVitals();
3. 编写组件功能
3.1 Count 组件 实例
实现一个数值 加减 的组件
(Count 组件)
-
src/redux/constants.js 文件修改,添加方法常量
// 定义action对象中type类型的常量值 export const INCREMENT = "increment"; export const DECREMENT = "decrement";
-
src/redux/actions 文件夹新增
count.js
,对应 Count 组件 的 actionimport { INCREMENT, DECREMENT } from "../constants.js"; // 加 // 简写方式 // export const increment = (data) => ({ type: INCREMENT, data }); // 正常方式 export const increment = (data) => { return { type: INCREMENT, data, }; }; // 减 export const decrement = (data) => { return { type: DECREMENT, data, }; };
-
src/redux/reducers 文件夹新增
count.js
,对应 Count 组件 的 reducer/** * 1. 该文件是用于创建一个为为Count组件服务的reducer,reducer的本质是一个函数 * 2. reducer函数会接收到两个参数,分别为:之前的状态(preState),动作对象(action) */ import { DECREMENT, INCREMENT } from "../constants.js"; const initState = 0; //初始化状态 // 当第一次 preState 为 undefined 时,preState 赋值等于 initState export default function increment(preState = initState, action) { // 从 action 对象中获取:type,data const { type, data } = action; // 根据 type 决定加工数据 switch (type) { case INCREMENT: return preState + data; case DECREMENT: return preState - data; default: return preState; } }
-
src/redux/reducers/index.js 文件修改,添加合并 Count 组件 的 reducer
-
Count 组件中 引入
connect
生成一个容器组件,并暴露。通过this.props....
读取和操作状态。容器组件写法:
export default connect( (state) => ({ key: value }),//映射状态 { key: action方法, }//映射操作状态的方法 )(UI组件);
import React, { Component } from "react"; import { connect } from "react-redux"; import { increment, decrement } from "../../redux/actions/count"; class Count extends Component { incrementNumber = () => { this.props.increment(1); }; decrementNumber = () => { this.props.decrement(1); }; render() { console.log(this.props); return ( <div> <h4>当前数值:{this.props.num}</h4> <button onClick={this.incrementNumber}>+1</button> <button onClick={this.decrementNumber}>-1</button> </div> ); } } /** * 映射状态,组件中需要使用的 reducer 状态 * state.xxx key 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key */ const mapStateToProps = (state) => ({ num: state.count }); // 映射操作状态的方法,组件中需要使用的事件方法 const mapDispatchToProps = { increment, decrement, }; // 使用connect()()创建并暴露一个Count的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(Count);
3.2 Persons 组件 实例
实现一个人员信息列表 新增 功能
(Persons 组件)
步骤同 3.1 Conut 组件实例 相同
-
src/redux/constants.js 文件修改,添加方法常量
// 定义action对象中type类型的常量值 export const INCREMENT = "increment"; export const DECREMENT = "decrement"; export const ADD_PERSON = "add_person";
-
src/redux/actions 文件夹新增
persons.js
,对应 Persons 组件 的 actionimport { ADD_PERSON } from "../constants.js"; // 新增人员 export const addPerson = (data) => ({ type: ADD_PERSON, data });
-
src/redux/reducers 文件夹新增
persons.js
,对应 Persons 组件 的 reducerimport { ADD_PERSON } from "../constants.js"; const initState = [ { id: 1, name: "小明", age: 18, }, ]; export default function addPerson(preState = initState, action) { const { type, data } = action; // 根据 type 决定加工数据 switch (type) { case ADD_PERSON: return [data, ...preState]; default: return preState; } }
-
src/redux/reducers/index.js 文件修改,添加合并 Persons 组件 的 reducer
-
Persons 组件 中 引入
connect
生成一个容器组件,并暴露。通过this.props....
读取和操作状态。import React, { Component } from "react"; import { connect } from "react-redux"; // 引入 action import { addPerson } from "../../redux/actions/persons.js"; class Persons extends Component { add = () => { const name = this.nameNode.value; const age = this.ageNode.value * 1; const personObj = { id: this.props.personList.length + 1, name, age, }; this.props.addPerson(personObj); }; render() { console.log(this.props); return ( <div> <input ref={(cur) => (this.nameNode = cur)} type="text" placeholder="请输入姓名" /> <input ref={(cur) => (this.ageNode = cur)} type="number" placeholder="请输入年龄" /> <button onClick={this.add}>添加</button> <ul> {this.props.personList.map((v) => { return ( <li key={v.id}> 姓名:{v.name},年龄:{v.age} </li> ); })} </ul> </div> ); } } /** * 映射状态,组件中需要使用的 reducer 状态 * state.xxx key 对应 src/redux/reducers/index.js 中 allReducer 对应组件的 key */ const mapStateToProps = (state) => ({ personList: state.persons }); // 映射操作状态的方法,组件中需要使用的事件方法 const mapDispatchToProps = { addPerson, }; // 使用connect()()创建并暴露一个Persons的容器组件 export default connect(mapStateToProps, mapDispatchToProps)(Persons);
3.3 Count组件 和 Persons 组件 相互通信
-
Count组件
mapStateToProps
中映射 Person组件 的状态
render()
中渲染
-
Persons组件
mapStateToProps
中映射 Count组件 的状态
render()
中渲染