Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux通常结合react一起使用,但除了react还可以与其他框架一起使用,如:angular、vue等;
一、安装
npm install redux -s
二、基础
下面将以一个操作用户和商品的功能为例说明。
项目最后完成工程结构:
store
actionTypes.js // 存放行为常量
index.js // redux工程入口,创建store
actions // 存放各模块action
Goods.js
User.js
reducers // 存放各模块reducer
Goods.js
User.js
1、创建行为常量文件
创建一个存放行为常量文件,提供 action 和 reducer 导入使用。
// actionTypes.js
export const ADD_USER = "ADD_USER"; // 添加用户
export const UPDATE_USER = "UPDATE_USER"; // 更新用户
export const DELETE_USER = "DELETE_USER"; // 删除用户
export const ADD_GOODS = "ADD_GOODS"; // 添加商品
export const UPDATE_GOODS = "UPDATE_GOODS"; // 更新商品
export const DELETE_GOODS = "DELETE_GOODS"; // 删除商品
2、创建action:
action是一个是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过store.dispatch() 将 action 传到 store。
我们约定,action一般包含type字段,type一般为常量,我们定义在actionTypes.js中。action还会需要一个载体属性,我们以payload作为载体属性,传递数据给reducer。
redux一般建议我们在项目较大时应该以模块分割action
actions/User.js:
// actions/User.js
import { ADD_USER, UPDATE_USER, DELETE_USER } from "../actionTypes"; // 按需导入action类型
// 添加用户
export function addUser(userName,gender,age) {
return {
type: ADD_USER,
payload: {
userName,
gender,
age
}
}
}
// 更新用户
export function updateUser(userName,gender,age) {
return {
type: UPDATE_USER,
payload: {
userName,
gender,
age
}
}
}
// 删除用户
export function deleteUser(userName,gender,age) {
return {
type: DELETE_USER,
payload: {
userName,
gender,
age
}
}
}
actions/Goods.js
// actions/Goods.js
import {ADD_GOODS, UPDATE_GOODS, DELETE_GOODS} from "../actionTypes"; // 按需导入action类型
// 添加商品
export function addGoods(id, goodsName, price, stock) {
return {
type: ADD_GOODS,
payload: {
id,
goodsName,
price,
stock
}
}
}
// 更新商品
export function updateGoods(id, goodsName, price, stock) {
return {
type: UPDATE_GOODS,
payload: {
id,
goodsName,
price,
stock
}
}
}
// 删除商品
export function deleteGoods(id, goodsName, price, stock) {
return {
type: DELETE_GOODS,
payload: {
id,
goodsName,
price,
stock
}
}
}
3、创建reducer:
Reducers 指定了应用状态的变化如何响应 action 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
reducer接收两个参数,第一个参数为旧的state, 第二个参数为action中载体payload,即新的state。
注意:我们不应该直接修改旧的state,我们始终应该返回一个新的state对象,我们应该对就state进行拷贝,然后在拷贝数据基础上进行修改。这是redux能够实现数据时光旅行的原因。
redux强调,我们不应该创建多个store,我们应该分割多个reducer来管理属于自己的部分,然后通过合并为一个reducers:
reducers/User.js
// reducers/User.js
import { ADD_USER, UPDATE_USER, DELETE_USER } from "../actionTypes"; // 按需导入action类型
const localState = [ // 初始数据,默认没有数据,用于测试
{userName: '张三', gender: '男', age: 24},
{userName: '李四', gender: '男', age: 25},
]
export default function(state = localState, action) {
switch (action.type) {
case ADD_USER:
return [...state, action.payload];
case UPDATE_USER:
return state.map(item => item.userName === action.payload.userName ? action.payload : item);
case DELETE_USER:
return state.filter(item => item.userName !== action.payload.userName);
default:
return state;
}
}
reducers/Goods.js
// reducers/Goods.js
import {ADD_GOODS, UPDATE_GOODS, DELETE_GOODS} from "../actionTypes";
const localState = [
{id: 1, goodsName: '小米10', price: 3999, stock: 10000},
{id: 2, goodsName: '小米10Pro', price: 4999, stock: 2000},
]
export default function(state = localState, action) {
switch (action.type) {
case ADD_GOODS:
return [...state, action.payload];
case UPDATE_GOODS:
return state.map(item => item.id === action.payload.id ? action.payload : item);
case DELETE_GOODS:
return state.filter(item => item.id !== action.payload.id);
default:
return state;
}
}
4、创建index.js
此文件用于整合reducer,并创建store,
import { createStore, combineReducers } from "redux";
import User from './reducers/User'; // 导入reducer的User模块
import Goods from './reducers/Goods'; // 导入reducer的Goods模块
let reducers = combineReducers({User,Goods}) // 合并reducer
let store = createStore(reducers); // 创建store,如无需合并,可直接传入reducer,如:createStore(User)
const unsubscribe = store.subscribe(() => { // 订阅state的数据监听。订阅后会返回一个函数,我们可以通过对其调用取消订阅。
console.log(store.getState())
})
export default store;
// unsubscribe(); // 取消订阅
combineReducers :合并多个reducer。
store提供了一下方法:
getState():获取当前state
dispatch(action):更新state
subscribe(listener):注册state监听。
到此所需的都已准备好,我们可以在任何一个页面通过store对其进行调用。如下示例执行了数据的增删改:
import store from './store/index'
import { addUser, updateUser, deleteUser } from './store/actions/User' // 获取所需的用户action
import {addGoods} from "./store/actions/Goods";
store.dispatch(addUser("王五", "男", 20)); // 添加一个用户
store.dispatch(updateUser("李四", "男", 220)); // 更新一个名称为李四的用户
store.dispatch(deleteUser("李四", "男", 22)); // 删除名为李四的用户
store.dispatch(addGoods(3, "红米K30 5G", 1999, 2000)); // 添加一个商品
三、搭配react
1、安装react-redux
npm install react-redux -s
2、react-redux 两个核心(Provider、connect)
Provider:这是一个组件,一般包裹在所有组件的最外层,并传递一个store,挂载到全局context,被包裹的组件可使用connect方法获得store。
<Provider store={store}>
...
</Provider>
connect:用于对组件与redux store的数据进行连接。一般需要被Provider包裹的组件才可以使用 connect,实在不想使用provider也是可以的,实际上对组件挂载了store就可以使用connect,官方建议使用Provider,否则你需要对每一个需要使用connect的组件进行一次store的props数据传递。
connect的使用方法如下:
connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(MyComponent)
connect方法可传递四个参数,connect调用后返回的依然是一个方法,接受一个参数,这个参数就是你需要使用connect的组件。
connect接受的四个参数:
参数 | 参数类型 | 说明 |
mapStateToProps(state, [ownProps]) | 方法 | 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps ,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用(例如,当 props 接收到来自父组件一个小小的改动,那么你所使用的 ownProps 参数,mapStateToProps 都会被重新计算) |
mapDispatchToProps | 方法或对象 | 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch 方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中 |
mergeProps(stateProps, dispatchProps, ownProps) | 方法 | 如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果 |
options | 对象 | 如果指定这个参数,可以定制 connector 的行为。
|
connect使用示例:
import React from 'react'
import {connect} from 'react-redux'
import {addGoods} from '../store/actions/Goods'
class Page extends React.Component {
render() {
return (
<div>
<button onClick={this.props.testClick}>添加商品</button>
</div>
)
}
}
const mapStateToProps = state => { // state:相当于store.getState()的数据
return { // 返回的对象将合并到Page所接收的props中。
goodsList: state.Goods
}
}
const mapDispatchProps = dispatch => { // dispatch:store.dispatch分发action方法。
return {
testClick: ()=>{ // 返回的方法将合并到Page组件的props中。
dispatch(addGoods(3, "红米K30 5G", 1999, 2000));
}
}
}
export default connect(mapStateToProps, mapDispatchProps)(Page);
redux官方中文文档:https://www.redux.org.cn/
注:此文章为个人笔记,不作标准答案,仅供参考,请以官方文档为准。