一. Redux介绍
1. 1 背景
当前端项目越来越庞大,使用到的诸如缓存数据、服务端数据、本地持久化数据也就越来越多,mode的变化会引起view的更新,而mode的来源是不确定,经常是错综复杂,甚至十分混乱的,遇到错误时很难排查和追踪。你可能使用过React中的context(上下文)把状态提升到顶层,被所有的子组件共享,这样还避免了props的层层传递。Redux也是一款类似的状态管理工具,不过它的功能更加全面,它是独立的,不仅仅可以应用在react中,还可以应用在javaScript项目中。
1.2. 核心思想
Redux的核心思想十分简单,就是一个简单的发布订阅,同时限定了只能通过dispatch(action)的方式更新数据,使数据的变化具有可预测性。
1.3 . 概念
- store 唯一的数据存储中心,其中还包含一些封装过的方法,例如getState和subsSribe
const store = createStore(reducer,initState);
- action 通常是一个对象,定义要以哪种方式(reducer)改变数据
const ADD = 'ADD';
export function add(id, text) {
return { type: ADD, id, text };
};
- dispatch 更新数据的唯一方式
dispatch(action);
- reducer 定义如何改变数据,是一个纯函数
function reducer(state = {}, action) {
...,
return newState;
}
- subScribe 订阅数据的方法,数据变化时执行
store.subscribe(()=>{
console.log(`新的值为`,store.getState());
});
1.4 Redux的三大原则
- 单一数据源
- 数据只读,只能通过dispatch的方式去更改数据
- reducer是一个存函数,不能带有副作用
二、基础使用
2.1 安装
yarn add redux --save
2.2 订阅数据
action.js
const ADD = 'ADD';
const REMOVE = 'REMOVE';
const SET_COMPLETE = 'SET_COMPLETE';
export function add(id, text) {
return { type: ADD, id, text };
};
export function remove(id) {
return { type: REMOVE, id };
};
export function setComlete(id, complete) {
return { type: SET_COMPLETE, id, complete }
};
export default { ADD, REMOVE, SET_COMPLETE, add, remove, setComlete };
reducer.js
import actions from './action'
// 注意reducer必须是一个存函数
function AppReducer(state = {}, action) {
const { id, text } = action;
const newTodoList = [...state.todoList];
switch (action.type) {
case actions.ADD: return { todoList: [...newTodoList, { id, text, complete: false, }] };
case actions.REMOVE: return { todoList: newTodoList.filter(t => t.id !== id) };
case actions.SET_COMPLETE: return { todoList: newTodoList.map(t => t.id === action.id ? t : { ...t, complete: action.complete }) };
default: return state;
}
};
export default AppReducer;
store.js
import reducer from './reducer';
import { createStore } from 'redux';
const initState = {
todoList:[
{id:'1',text:'游泳',complete:false},
{id:'2',text:'冥想',complete:false},
{id:'3',text:'工作',complete:false},
{id:'4',text:'午休',complete:false},
{id:'5',text:'追番',complete:false},
]
}
const store = createStore(reducer,initState);
export default store;
List.jsx // 使用redux实现一个TodoList
import { memo, useCallback, useEffect, useState } from 'react';
import { Input, Button } from 'antd';
import store from './store';
import {add, remove} from './action';
function List() {
const [id, setId] = useState('0');
useEffect(()=>{
store.subscribe(()=>{
console.log(`新的值为`,store.getState());
});
},[]);
const onAdd = useCallback(() => {
store.dispatch(add(id,`新增数据${id}`));
}, [id]);
const onRemove = useCallback(() => {
store.dispatch(remove(id));
}, [id]);
const onInputChange = useCallback((e) => {
setId(e.target.value);
}, []);
return (
<div className="App">
<span>id: <Input onChange={onInputChange} value={id} type='text' /></span>
<Button onClick={onAdd}>增加</Button>
<Button onClick={onRemove}>删除</Button>
</div>
);
}
export default memo(List);
上述代码中使用subScribe函数订阅了store,当store中的数据发生变化的时候会执行回调函数
2.3 搭配react进行视图的渲染和更新
上述代码中我们只监听了数据的变化,但是如何将数据的变化和视图的渲染关联到一起呢?这个就是react-redux的职责了,只需要简单两个步骤就可以将redux的数据和视图关联起来。
第一步: 安装react-redux
yarn add react-redux
第二部: 改造List.jsx
// List.jsx
import { memo, useCallback, useState, useMemo } from 'react';
import { Input, Button } from 'antd';
import { add, remove } from './action';
import { connect } from 'react-redux';
// 将redux的state映射到props
const mapStateToProps = (state) => {
return { todoList: state.todoList };
};
// 将redux的dispatch映射到props
const mapDispatchToProps = (dispatch) => {
return {
onAdd: (id) => dispatch(add(id, `新增数据${id}`)),
onRemove: (id) => dispatch(remove(id)),
}
};
// 实现一个TodoList
function List(props) {
const [id, setId] = useState('0');
const onInputChange = useCallback((e) => {
setId(e.target.value);
}, []);
const renderList = useMemo(() => {
return props.todoList.map(todo => {
return <div style={{ marginTop: '20px' }} key={todo.id}>
<div>名称:{todo.text}</div>
<div>ID:{todo.id}</div>
<div>状态:{todo.complete ? '完成' : '未完成'}</div>
</div>
})
}, [props.todoList]);
return (
<div className="App" style={{ textAlign: 'center' }}>
<span>id: <Input onChange={onInputChange} value={id} type='text' /></span>
<Button onClick={() => props.onAdd(id)}>增加</Button>
<Button onClick={() => props.onRemove(id)}>删除</Button>
{renderList}
</div>
);
}
export default memo(connect(mapStateToProps, mapDispatchToProps)(List));
// App.jsx
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import store from './store';
import List from './List';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<List />
</Provider>
)
}
}
- provider的作用是将store注入到全局,方便子组件中获取
- connect是一个高阶组件,作为容器组件将state和dispatch注入到展示组价,connect接收mapStateToProps和mapDispatchToProps两个参数,如果不传递state默认为store中的全部数据,dispatch则为store.dispatch
三. 使用combineReducers拆分reducer
当数据过多的时候可以对reducer进行拆分,上面的例子中我们只有一个todoList,现在我们来添加一个存储用登录信息的数据。
// reducer.js
export function todoListReducer(state = [], action) {
const { id, text } = action;
const newTodoList = [...state];
switch (action.type) {
case actions.ADD: return [...newTodoList, { id, text, complete: false, }];
case actions.REMOVE: return newTodoList.filter(t => t.id !== id);
case actions.SET_COMPLETE: return newTodoList.map(t => t.id === action.id ? t : { ...t, complete: action.complete });
default: return state;
}
};
export function userInfoReducer(state = {}, action) {
switch (action.type) {
case actions.ADD_USERINFO: return action.userInfo;
case actions.REMOVE_USERINFO: return {};
default: return state;
}
};
// store.js
import { userInfoReducer, todoListReducer } from './reducer';
import { createStore, combineReducers } from 'redux';
const initState = {
todoList: [
{ id: '1', text: '游泳', complete: false },
{ id: '2', text: '冥想', complete: false },
{ id: '3', text: '工作', complete: false },
{ id: '4', text: '午休', complete: false },
{ id: '5', text: '追番', complete: false },
],
userInfo: {},
};
const store = createStore(combineReducers({ todoList: todoListReducer, userInfo: userInfoReducer }), initState);
export default store;
下一章讲述redux中间件的使用,以及中间件的思想