一、Redux 基本思想
这里给出一个图来说明 redux 的主要三个部分构成。
1. Action:
用来表示需要对 state 进行什么样操作的数据。主要的数据结构如下:
let action = {
type: 'xs',
payload: {
name: 'xxx',
},
};
其中,type 主要描述你当前这个 action 的名称,方便后续进行 state 操作的逻辑处理。
Redux 允许我们去在 action 对象中定义其他的数据。比如上面的 action.payload 属性。
也就是说,除了 type 是必须存在的属性(用来描述当前操作的类型),我们可以在 action 中定义其他任意类型的属性。但是 Redux 还是希望我们能够尽量将 action 设计的简洁一些。
2. Reducer:
用来告诉 state,遇到不同类型的 aciton 的时候,应该做什么样的操作。
可以简单概括成是这样一个函数:(currState, action) => nextState。根据当前的 state 和 action,获得更改之后得到的 state。
一开始我们可以只设计一个 reducer,让这个 reducer 函数来负责整个 state 的变换。比如下面这样:
const reducer = (state, action)=>{
// 判断是哪种类型的 action
if(action.type === 'changeName'){
// 更改姓名 操作
/* 由于 redux 约定不允许改变原有 state(数据不可变)
所以使用 Object.assign 来生成一个新的 state */
return Object.assign({}, state, {
name: action.payload.name,
});
}
if(action.type === 'xxx'){
// 进行别的操作
}
}
随着我们应用的增长,为了方便管理,可以将 reducer 函数拆分出来,设计多个小的 reducer 函数,每个 reducer 函数来控制一部分的 state 的变化。
之后将这些小的 reducer 函数在根 reducer 的函数中进行应用。基本的模式如下所示:
// 对 state.name 负责的 reducer 函数
const nameReducer = (state, action)=>{
if(action.type === 'changeName'){
return action.payload.name;
}
};
// 对 state.country 负责的 reducer 函数
const countryReducer = (state, action)=>{
if(action.type === 'xxx'){
// 逻辑操作,返回部分 state
}
};
// 整体 reducer
const reducer = (state, action)=>{
return {
name: nameReducer(state.name, action),
country: countryReducer(state.country, action),
};
};
Redux 也给出了自己的 api combineReducers 函数,方便我们去使用:
import { combineReducers } from 'redux';
// 小的 reducer 函数,用来管理部分 state
import { addPeople } from './addPeople.js';
import { setCOuntry } from './setCountry.js';
const reducerObj = {
people: addPeople,
country: setCountry,
};
const reducers = combineReducers(reducerObj);
export default reducers;
后面我们会用到 createStore api,这个函数接收一个 reducer 函数作为参数,创建出 store。
这里就会通过传入对象的 key 值来找到 state 中对应的部分。比如,使用到 setCountry 的时候,就会改变 state.country 的值。
后面在描述整个数据流的过程中,会详细说到这里 根 reducer 函数的 key & value 的作用。
3. Store:
主要负责:
state的存储- 获取部分
state的值:使用getState()函数 - 更新 state:使用
dispatch(action)函数 - 注册监听器:使用
subscribe(listener)函数 - 注销监听器:使用
subscribe(listener)返回的函数注销
可以使用 Redux 的 api 来创建 store,来管理 state。需要做的事情是下面几步:
import { createStore } from 'redux';
import reducer from './reducers';
let store = createStore(reducer); // 将 store 和对应的 根 reducer 连接起来
(目前应该先使用前三个api来简单体验一下 redux 的使用)
4. Redux 的基本使用:
目前我们已经知道,使用 Redux 需要创建的基本的三个组成部分。下面来简单看一下如何将 redux 和你已有的 react 组件结合起来:
文件结构:
/src
index.js // 入口文件,本例子中会是一个简单的 react 组件
/actions
index.js // 返回对应的 action 对象的函数
/reducers
index.js // 总 reducer,其中调用各种 reducer 函数
addPeople.js // 子 reducer,管理 state.people
/store
index.js // 存储 store 的位置
具体的代码文件如下所示:
组件
/src/index.js
import React from "react";
import PropTypes from "prop-types";
import { connect } from 'react-redux';
import { store } from './store/index.js';
import { addPeople } from './actions/index.js';
function TestApp(props) {
const { dispatch } = props; // 获取 dispatch 函数,用来改变 store 中的 state
// 当 button 点击事件发生的时候,改变 state.people 中的信息
onAddPeopleCLick = () => {
// 触发 addPeople action
dispatch(addPeople({
name: 'jack',
age: 20,
}))
}
return (
// 注入 store
<Provider store={store}>
{/* 显示人数 */}
<div id='peopleNum'>peopleNum: {props.people.length}</div>
{/* 增加人数 按钮 */}
<button onClick={onAddPeopleCLick}>add people</button>
</Provider>
);
}
TestApp.propTypes = {};
// 使用 connect 函数,将 store 中保存的 state 注入到 props 中,以便我们使用 store 中的信息
// 第一个箭头函数表示如何将 state 映射到这个新组件的 props 上
// connect 函数返回一个由原有组件,经过注入新的 props之后,包装好的新组件
export default connect((state)=>{
// 返回的内容就是我们即将注入 props 的内容
return {
people: state.people,
};
})(TestApp);
定义 action
/src/actions/index.js
// 增加人数
export const addPeople = (peopleInfo) => {
return {
type: 'ADD_PEOPLE',
payload: peopleInfo,
};
}
Reducer 定义
/src/reducer/index.js
// 总 reducer
import { combineReducers } from 'redux';
import { addPeople } from './addPeople.js';
const reducers = combineReducers({
people: addPeople,
});
export default reducers;
/src/reducer/addPeople.js
export const addPeople = (state, action) => {
if(action.type === 'ADD_PEOPLE'){
return [
...state,
action.payload, // 新增人员的信息
]
}
}
store 定义:
/src/store/index.js
import { createStore } from 'redux';
import { reducers } from './reducer/index.js';
export let store = createStore(reducers);
二、补充:redux 中一些api的具体作用
1. combineReducers 函数:
combineReducers(reducerObj)
- 接收一个对象
reducerObj,会根据这个对象的 key 来寻找 state 中对应的部分,value 中的函数都是负责state[key]的对应的内容。
combineReducers({
people: addPeople,
country: changeCountry,
});
// 最后得到的state的形式:
/**
{
people: [],
country: 'xx',
}
*/
// 增加 个人信息
dispatch({
type: 'ADD_PEOPLE',
payload: {
name: 'jack',
age: '20',
}
});
// 最后得到的state:
/**
{
people: [
{
name: 'jack',
age: '20',
},
],
country: 'xx',
}
*/
2. createStore 函数:
createStore(reducer, initialState)
reducer:- 接收一个 reducer 函数,这个函数告诉 strore,根据现在的 state 和 action,如何得到下一步的 state。
initialState:存储的全局 state 的初始值
3. connect 函数:(待更新)
connect(mapStateToProps, mapDispatchToProps,)
mapStateToProps: 函数,
4. 从 dispatch action 到 state 变化 发生了什么(切分了多个reducer):
下面给出一张图说明一下,从 dispatch 函数触发的时候,到最后 state 更改的时候,都是怎么进行变化的。
- 在 dispatch 函数被调用的时候,根 reducer 函数被调用。
- 会触发下面每一个 子 reducer 函数
- 将子 reducer 函数得到的内容进行合并,形成新的 state。每个子 reducer 函数返回的内容,都会作为根 reducer 定义时,子 reducer 函数的 key 下面的内容。
next step: 了解一下 redux 中的异步操作,以及 redux 背后的运作数据流问题。目前我正在使用 redux-toolkit,下次来总结一下这个东西的简单用法