什么情况下用Redux
明确一点,Redux是一个有用的框架,但不是非用不可。事实上,大多数情况下,你可以不用他,只用React就够了。
- 用户的使用方式非常简单
- 用户之间没有协作
- 不需要与服务器大量交互,也没有使用 WebSocket
- 视图层(View)只从单一来源获取数据
上面这些情况,都不需要使用Redux
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSocket
- View要从多个来源获取数据
上面这些情况才是Redux的适用场景:多交互,多数据源
从组件的角度看,如果你的应用有以下场景,可以考虑使用Redux
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
总之,不要把Redux当做万灵丹,如果你的应用没那么复杂就没必要用它。
Redux的三个基本原则
单一数据源
整个应用的state被储存在一棵object tree,并且这个object tree只存在于唯一的store中
State是只读的
唯一改变state的方法就是触发action,action是一个用于描述已经发生事件的普遍对象。
使用纯函数来执行修改
为了描述action如何改变state tree,你需要并编写reducers
基本概念和API
Store
store就是保存数据的地方,你可以把它看成容器,整个应用只能有一个Store。
import { createStore } from 'redux';
const store = createStore(fn);
Redux提供createStore这个函数,用来生成Store
State
Store对象包含所有数据。如果想得到某个时点的数据,就要对Store生成快照。 这种时点的数据的集合,就叫做State。当前时刻的State,可以通过 store.getState()拿到。
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();
Redux规定,一个State对应一个View。只要State相同,view就相同。
Action
State的变化会导致View的变化,但是,用户接触不到State,只能接触到View。 所以,State的变化必须是View导致的。Action就是View发出的通知,表示State应 该要变化了。
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux'
};
Action是一个对象。其中的type属性是必须的,表示Action的名称。其他属性可 以自由设置。
Action描述当前发生的事情。改变State的唯一办法,就是使用Action。它会运送 数据到Store。
Action Creator
View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。
const ADD_TODO = '添加 TODO';
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('Learn Redux');
上述代码中,addTodo函数就是一个Action Creator
store.dispatch()
store.dispatch()是View发出Action的唯一方法
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
store.dispatch()接受一个Action对象作为参数,将他发送出去
Reducer
store收到Action以后,必须给出一个State,这样View才会发生变化。这种State的计算过程就叫做Reducer。
Reducer是一个函数,他接受Action和当前State作为参数,返回一个新的State
const reducer=function (state,Action){
//....
return new_state;
}
整个应用的初始状态,可以作为State的默认值。
const defaultState=0;
const reducer=(state=defaultState,action)=>{
switch (action.type){
case "ADD":
return state + action.payload;
default:
return state;
}
}
const state=reducer(1,{
type:"ADD",
payload:2
});
上面代码中,reducer函数收到名为ADD的Action以后,就返回一个新的State,作为加法的计算结果。
实际应用中,Reducer函数是不用像上面这样手动调用的,store.dispath方法会触发Reducer的自动执行。为此Store需要知道Reducer函数,做法就是在生成Store的时Reducer传入createrStore方法。
import { createStore } from 'redux'
const stor = createStore(reducer)
上面代码中,createStore接受Reducer作为参数,生成一个新的Store。以后每当store.dispath发过来一个新的Action,就会自定调用Reducer,得到新的State。
Reducer还可以作为数组的reduce方法(reduce方法是使数组累加)的参数。
const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];
const total = actions.reduce(reducer, 0); // 3
上面代码中,数组action表示一次有三个Action,分别是0,加1和加2。数组的reudce方法接受Reducer函数作为参数,就可以得到最终的状态。
纯函数
Reducer函数最重要的特征是,他是一个纯函数,也就是说,只要是同样的输入,必得到同样的输出。
纯函数是函数是编程的概念,必须遵守以下一些约束
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用`Date.now()`或者`Math.random()`等不纯的方法,因为每次会得到不一样的结果
由于Reducer是纯函数,就可以保证同样的State,必定得到同样的View。但也正是因为这一点,Reducer函数里面不能改变State,必须返回一个全新的对象
//State是一个对象
function reducer(state, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
return [...state, newItem];
}
最好把State对象设成只读。你没法改变它,要得到新的State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个View对应的State总是一个不变的对象。
store.subscribe()
Store允许store.subscribe方法设置监听函数,一旦State发生变化,就自动执着
这个函数。
import { createStore } from 'redux'
const store=createStore(reducer);
store.subscribe(lister);
显然,只要把View的更新函数(对于React项目,就是组件的render方法或setState方法)放入listen,就会实现View的自动渲染。
store.subscrible方法返回一个函数,调用这个函数就可以解除监控。
let unsubscrible=store.subscrible(()=>
console.log(store.getState());
);
Store的实现
Store提供了三个方法
- store.getState()//获得当前State
- store.dispatch()//从view发出Action的唯一方法
- store.subsrcible()//一旦State发生变化,就调用()里的函数
下面是createStore方法的一个简单实现,可以了解一下Store是怎么生成的。
const createStore=(reducer)=>{
let state
let listeners=[]
const getState=()=>state
const dispatch=(action)=>{
state=reducer(state,action)
listeners.forEach(listener=>listener())
}
const subscribe=(listener)=>{
listrners.push(listener)
return()=>{
listeners=listeners.filter(l=>l !==listener)
}
}
dispatch({})
return {getState,dispatch,subscribe}
}
上面代码很生动的写明白了,store的三个方法的生成过程,以及为啥调用dispatch的时候会自动调用reducer函数。
工作流程
首先,用户发出Action
store.dispatch(action)
然后Store会自动调用Reducer,并且传入两个参数,当前State和收到的Action。Reducer会返回一个新的State
let nextState=todoApp(State,action)
State一旦有变化,Store就会调用监听函数。
store.subscribe(listener)
listener可以通过store.getState()得到当前状态。如果使用的是React,这时可以出发重新渲染View。
function listener(){
let newState=store.getState()
component.setState({
newState
})
}