参考阮一峰博客:redux教程
一、react设计思想
- web应用是一个状态机,视图与状态是一一对应的。
- 所有状态都保存在一个对象里面。
二、redux基本概念
1. Store
Store是保存数据的地方,可以将其看成一个容器,整个应用只能有一个Store.
2. State
State是Store对象的某个时点的数据集合,当前时刻的State可以通过store.getState()得到。
3. Action
State和View一一对应,Action就是View发出的通知,表示State应该要发生变化了。
Action是一个对象,其中的type属性是必须的,表示Action的名称。
注:改变State的唯一办法就是使用Action.
4. Action Creator
它是用以定义一个函数来生成Action
const ADD_TODO = '添加 TODO';
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('Learn Redux');
5. store.dispatch()
它是View发出Action的唯一方法,接受一个Action对象作为参数并将它发送出去。
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
6. Reducer
Reducer是一个函数,它接受Action和当前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
});
注:实际应用中,store.dispatch方法会触发Reducer的自动执行,因此createStore可接受Reducer作为参数生成一个新的Store.
7. 纯函数
Reducer函数最重要的特征是一个纯函数,即同样的输入必定得到同样的输出。
纯函数是函数式编程的概念,必须遵守以下约束:
1. 不得改写参数
2. 不能调用系统I/O的API
3. 不能调用Date.now()或者Math.random()等不纯的方法,因为每次都会得到不一样的结果
注:Reducer函数里面不能改变State,必须返回一个全新的对象。
// State 是一个对象
function reducer(state, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
return [...state, newItem];
}
8. store.subscribe()
Store允许使用store.subscribe方法设置监听函数,一旦State发生变化,就自动执行这个函数。
//将View的更新函数放入listen可以实现View的自动渲染
import { createStore } from 'redux';
const store = createStore(reducer);
store.subscribe(listener);
//store.subscribe方法返回一个函数,调用这个函数就可以解除当前listener监听
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
unsubscribe();
三、Store的实现
store提供了三个方法:store.getState()/store.dispatch()/store.subscribe()
import { createStore } from 'redux';
let { subscribe, dispatch, getState } = createStore(reducer);
注:createStore方法还接受第二个参数,表示state的最初状态,它会覆盖Reducer函数的默认初始值。
//createStore方法的简单实现
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
};
四、 Reducer的拆分
Redux提供了一个combineReducers方法用于Reducer的拆分。
import { combineReducers } from 'redux';
const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
})
export default todoApp;
注:state属性名必须与子Reducer同名,combineReducers()函数会根据State的key去执行相应的子Reducer,并将返回结果合并成一个大的state对象。
//combineReducer的简单实现
const combineReducers = reducers => {
return (state = {}, action) => {
return Object.keys(reducers).reduce(
(nextState, key) => {
nextState[key] = reducers[key](state[key], action);
return nextState;
},
{}
);
};
};
五、工作流程
- 用户发出Action.
store.dispatch(action);
- Store自动调用Reducer,且传入两个参数,当前State和收到的Action,Reducer会返回新的State.
let nextState = todoApp(previousState, action);
- State一旦发生变化,Store会调用监听函数.
store.subscribe(listener);
- listener通过store.getState()得到当前状态,若用react,则会触发重新渲染View.
function listerner() {
let newState = store.getState();
component.setState(newState);
}
六、实例:计数器
功能:为计数器添加递增和递减的Action,并将参数value的值显示在网页上,Store的监听函数设置为render,每次State的变化都会导致网页重新渲染。
const Counter = ({ value, onIncrement, onDecrement }) => (
<div>
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
);
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);