Redux: JavaScript 状态容器,提供可预测化的状态管理
需要使用Redux 的情景:
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
Redux设计思想:
(1)Web 应用是一个状态机,视图与状态是一一对应的。
(2)所有的状态,保存在一个对象里面。
API:
*1. Store容器:保存所有数据,整个应用只能有一个Store
const store = createStore(fn, window.STATE_FROM_SERVER);
//接受另一个函数作为参数,生成返回新的Store对象。第二个参数通常是服务器给出,表示整个应用的初始状态值,会覆盖Reducer函数的默认初始值
*2. State对象:Store快照,某一时间点的数据集合,(规定State和View唯一对应,知其一就可推断另一个)
const state = store.getState(); //拿到当前时刻的State
*3. Action对象:描述当前发生的事情,用户改变界面后,改变State的唯一办法,Action会运送数据到Store
const action = {
type: 'ADD_TODO', //必须,表示Action名称
payload: 'learn redux' //携带的信息
}
*4. Action Creator函数:生成Action,解决多个View手动生成多个Action的麻烦
const ADD_TODO = 'add TODO';
function addTodo(text) { //Action Creator
return {
type: ADD_TODO,
text
}
}
const action = addTodo('learn');
*5. store.dispatch():View发出Action的唯一方法
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({ //将Action对象作为参数发送出去
type: 'ADD_TODO',
payload: 'learn'
}); //可以是: store.dispatch(action);结合Action Creator
*6. Reducer函数:Store 收到Action后,须给出新的State来改变View,新State 的计算过程就是Reducer
const defaultState = 0;
const reducer = (state = defaultState, action) => { //接受旧State和收到的antion作为参数
switch (action.type) {
case 'ADD':
return state + action.payload; //返回新的State
default:
return state;
}
}
//手动调用
const state = reducer(1, action);
//自动调用
import { createStore } from 'redux';
const store = createStore(reducer); //每当store.dispatch() 发送一个新的Action ,就会自动调用 reducer()
*7. 纯函数:同样的输入,必定得到同样的输出,有约束:
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now() 或者Math.random()等不纯的方法,每次会得到不一样的结果
//保证返回的是新对象或数组,不改变之前的State,使得某个State与某个View仍然唯一对应
function reducer(state, action) {
return Object.assign({}, state, { change }); // or return {...state, ...newState},State是对象
return [...state, newItem]; //State是数组
}
*8. store.subscribe(): 监听State的变化
store.subscribe(listener); //listener可以是React的render(), setState()方法,即可实现View 的自动渲染
let unsubscribe = store.subscribe(listener);unsubscribe();//即可解除监听
*9. combineReducers(): 合并子Reducer()成一个大的函数
/*
*假设:三种Action 分别改变State 的三个属性
*- ADD_C:c属性
*- CHANGE_S:s属性
*- CHANGE_U:u属性
*/
const chatReducers = (state = defaultState, action = {}) => { //为了避免庞大的State导致复杂的Reducer,所以Reducer的拆分
return { //各个子Reducer 函数均是自定义
c: c(state.c, action),
s: s(state.s, action),
u: u(state.u, action)
}
}
/*前提:State 属性名必须与子 Reducer 同名*/
import { combineReducers } from 'redux';
const chatReducer = combineReducers({ //合并上面对 Reducer 的拆分
c,
s,
u
})
Redux工作流程
中间件(middleware):为实现Reducer异步操作
*1. applyMiddlewares(): 将所有中间件组成一个数组,依次执行,所以加载时,需要查询中间件的执行顺序
*2. 异步操作:
//三种Action(发起时,成功时,失败时)
// 写法一:名称相同,参数不同
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
// 写法二:名称不同
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }
//State三种属性
let state = {
isFetching: true,//是否抓取数据
didInvalidate: true,//数据是否过时
lastUpdated: 'xxxxxxx'//上一次更新时间
};
思路:操作开始时,送出一个 Action,触发 State 更新为"正在操作"状态,View 重新渲染;操作结束后,再送出一个 Action,触发 State 更新为"操作结束"状态,View 再一次重新渲染
*3. redux-thunk中间件:异步操作,写出一个返回函数的 Action Creator,然后设applyMiddlewares为redux-thunk中间件改造store.dispatch,使其可接受函数参数
*4. redux-promise中间件: 异步操作,让Action Creator 返回一个Promise对象,设applyMiddlewares为redux-promise
React-Redux: 将所有组件分成两大类(UI组件和容器组件)
React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。
*1. UI组件:只负责 UI 的呈现,不带有任何业务逻辑;没有状态(即不使用this.state这个变量);所有数据都由参数(this.props)提供;不使用任何 Redux 的 API
*2. 容器组件:负责管理数据和业务逻辑,不负责 UI 的呈现;有内部状态;使用 Redux 的 API
*3. connect(): React-Redux提供,用于从UI组件生成容器组件,两组件建立联系
const VisibleTodoList = connect(
mapStateToProps, //输入逻辑:外部的数据(state)如何转换为UI组件的参数
mapDispatchToProps //输出逻辑:用户发出的动作如何变为Action对象,从UI组件传出去
)(TodoList);//TodoList是UI组件,由此生成了容器组件
*4. mapStateToProps(): 建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系
// 容器组件的代码
// <FilterLink filter="SHOW_ALL">
// All
// </FilterLink>
const mapStateToProps = (state, ownProps) => { //参数1:state对象,参数2:容器组建的props对象
return {
active: ownProps.filter === state.visibilityFilter
}
}
*5. mapDispatchToProps: 定义了哪些用户的操作应该当作 Action,传给 Store,可以是函数或者对象
//函数
const mapDispatchToProps = (dispatch,ownProps) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
});
}
};
}
//对象
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
*6. <Provider>组件:React-Redux提供,让容器组件拿到state
ReactDOM.render(
<Provider store={store}> //let store = createStore(reducer); Redux生成store对象
<App /> //在跟组件外包一层,则其所有子组件就默认都能拿到state了
</Provider>,
document.getElementById('root')
)
学习自:
Redux 入门教程(一):基本用法
Redux 入门教程(二):中间件与异步操作
Redux 入门教程(三):React-Redux 的用法