文章目录
1. Redux介绍
React只是一个DOM的抽象层,对于大型项目将会遇到组件之间的通信
和代码结构
等问题。而Redux将能解决状态管理问题,它用于中大型,数据比较庞大,组件之间数据交互多的情况下使用。但注意Redux的使用,Redux的创造者说过:
“如果你不知道是否需要 Redux,那就是不需要它。” “只有遇到 React 实在解决不了的问题,你才需要 Redux 。”
Redux适用以下场景:(多交互,多数据源)
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(比如普通用户和管理员)
- 多个用户之间可以协作
- View要从多个来源获取数据
组件的角度:
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
2.重要概念
2.1 Store
理解:数据仓库,保存数据的地方。整个应用只能有一个Store。
Redux提供了API:createStore
,用于生成Store。
示例:
import { createStore } from 'redux'
const store = createStore(func)
createStore
函数接收一个函数作为参数,返回新生成的Store对象。
2.2 State
理解:Store
对象包含所有数据。如果想要得到某个时间点的数据,就要对Store生成快照。这种时间点得数据集合,就叫做State。当前时刻的State可以通过store.getState()
获取。
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();
Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。
2.3 Action
理解:一个对象。当View的某个操作会导致state的数据发生改变时,那么View就会通过action去通知更新state。
示例:
const action = {
type:"事件名称",
payload:"xxxx"//该action携带的信息
}
由于View可以触发的改变state的状态的事件很多,而每个事件都需要一个Action,如果都手写,会很麻烦。可以定义一个函数来生成Action,这个函数就叫做Action Creator.
const ADD_TODO = '添加 TODO';
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('Learn Redux');
2.4 store.dispatch()
从上面我们知道action是一个对象
,那它想要在View将信息通知给State,就需要有人将action送过去吧,而store.dispacth()
是View发出Action的唯一方法。
示例:
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: '事件类型',
payload: 'xxx'
});
上面代码中,store.dispatch接收一个Action对象作为参数,将它发送出去。
2.5 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,作为加法的计算结果。其他运算的逻辑(比如减法),也可以根据 Action 的不同来实现。
实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch
方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore
方法。
import { createStore } from 'redux';
const store = createStore(reducer);
上面代码中,createStore
接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch
发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。
为什么这个函数叫做 Reducer
呢?因为它可以作为数组的reduce
方法的参数。请看下面的例子,一系列 Action 对象按照顺序作为一个数组。
const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];
const total = actions.reduce(reducer, 0); // 3
上面代码中,数组actions表示依次有三个 Action,分别是加0、加1和加2。数组的reduce方法接受 Reducer 函数作为参数,就可以直接得到最终的状态3。
2.6 store.subscribe()
Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。
import { createStore } from 'redux';
const store = createStore(reducer);
store.subscribe(listener);
显然,只要把 View 的更新函数(对于 React 项目,就是组件的render
方法或setState
方法)放入listen
,就会实现 View 的自动渲染。
store.subscribe
方法返回一个函数,调用这个函数就可以解除监听。
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
unsubscribe();
3. 工作流程
首先,用户发出 Action。
store.dispatch(action);
然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。
let newState = todoApp(oldState, action);
State 一旦有变化,Store 就会调用监听函数。
// 设置监听函数
store.subscribe(listener);
listener
可以通过store.getState()
得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
function listerner() {
let newState = store.getState();
component.setState(newState);
}
4.实例:计数器
下面我们来看一个最简单的实例。
const Counter = ({ value }) => (
<h1>{value}</h1>
);
const render = () => {
ReactDOM.render(
<Counter value={store.getState()}/>,
document.getElementById('root')
);
};
store.subscribe(render);
render();
上面是一个简单的计数器,唯一的作用就是把参数value
的值,显示在网页上。Store 的监听函数设置为render
,每次 State 的变化都会导致网页重新渲染。
下面加入一点变化,为Counter
添加递增和递减的 Action。
const Counter = ({ value, onIncrement, onDecrement }) => (
{value}
+ -const reducer = (state = 0, action) => {
switch (action.type) {
case ‘INCREMENT’: return state + 1;
case ‘DECREMENT’: return state - 1;
default: return state;
}
};
const store = createStore(reducer);
const render = () => {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => store.dispatch({type: ‘INCREMENT’})}
onDecrement={() => store.dispatch({type: ‘DECREMENT’})}
/>,
document.getElementById(‘root’)
);
};
render();
store.subscribe(render);