前言
为什么有使用
随着
JavaScript
单页应用开发日趋复杂,管理不断变化的state
变得十分困难
三大原则
- 【一】单一数据流
- 整个应用的
state
被储存在一棵object tree
中,并且这个object tree
只存在于唯一的store
中
- 整个应用的
- 【二】只读
state
- 唯一改变
state
的方法就是触发action
- 唯一改变
- 【三】使用
reducer
函数执行修改- 根据触发的
action
来通知reducer
函数来执行相应修改state
动作
- 根据触发的
设计思想
- 【一】将整个应用状态存储到到一个地方,称为
store
,里面保存一棵状态树state tree
- 【二】组件可以派发
dispatch
行为action
给store
,而不是直接通知其它组件 - 【三】其它组件可以通过订阅
store
中的state
来刷新自己的视图
基础使用
本文以一个小计数器为例子逐一讲解
原生版
- 【0】页面结构设计
<body>
<div id="counter">
<div id="counter-value"></div>
<button id="increment-btn">+</button>
<button id="decrement-btn">-</button>
</div>
</body>
- 【1】定义
action
动作
const INCREMENT = Symbol.for('INCREMENT');
const DECREMENT = Symbol.for('DECREMENT');
- 【2】创建
reducer
函数,用来管理状态
function reducer(state, action) {
switch (action.type) {
case INCREMENT:
return state + 1; // 返回一个加1的新状态
case DECREMENT:
return state - 1;// 返回一个减1的新状态
default:
return state;
}
}
- 【3】创建仓库
store
import {createStore} from 'redux';
let store = createStore(reducer,initState);
- 【4】挂载状态&&订阅状态
let counterValue = document.getElementById('counter-value');
let incrementBtn = document.getElementById('increment-btn');
let decrementBtn = document.getElementById('decrement-btn');
function render() {
counterValue.innerHTML = store.getState();
}
render();
let unsubscribe = store.subscribe(render);
- 【5】派发动作
incrementBtn.addEventListener('click',function () {
store.dispatch({
type:INCREMENT
})
})
decrementBtn.addEventListener('click',function () {
store.dispatch({
type:DECREMENT
})
})
- 【6】卸载状态(取消订阅状态)
setTimeout(()=>{
unsubscribe()
},3000)
React
版
- 【1】定义
action
动作
const INCREMENT = Symbol.for('INCREMENT');
const DECREMENT = Symbol.for('DECREMENT');
- 【2】创建
reducer
函数,用来管理状态
function reducer(state, action) {
switch (action.type) {
case INCREMENT:
return state + 1; // 返回一个加1的新状态
case DECREMENT:
return state - 1;// 返回一个减1的新状态
default:
return state;
}
}
- 【3】创建仓库`store`
```JS
import {createStore} from 'redux';
let store = createStore(reducer,initState);
- 【4】绑定
action
指派对象
const boundActions = bindActionCreators(actions,store.dispatch)
- 【5】创建组件
export default class Counter extends Component {
state = {number:store.getState()}
componentDidMount(){
this.unsubscribe=store.subscribe(()=>{
this.setState({
number:store.getState()
})
})
}
componentWillUnmount(){
this.unsubscribe()
}
render() {
return (
<>
<p>{this.state.number}</p>
<button onClick={boundActions.increment}>+</button>
<button onClick={boundActions.decrement}>-</button>
</>
)
}
}
原理进阶
createStore
- 参数:
reducer
,初始状态preloadedState
- 返回:
getState
subscribe
dispatch
- 源码实现
function createStore(reducer,preloadedState){
let currentReducer = reducer;
let currentState = preloadedState;
let currentListeners = [];
function getState(){
return currentState;
}
function dispatch(action){
if(!isPlainObject(action)){
throw new Error('action必须是一个纯对象');
}
if(typeof action.type === 'undefined'){
throw new Error('action必须有type属性')
}
currentState = currentReducer(currentState,action);
for(let i = 0;i<currentListeners.length;i++){
const listener = currentListeners[i];
currentListener();
}
return action;
}
function subscribe(listener){
currentListeners.push(listener);
return function(){
const index = currentListeners.indexOf(listener);
currentListeners.splice(index,1);
}
}
return {
getState,
dispatch,
subscribe
}
}
bindActionCreators
- 参数:
actionCreators
,dispatch
- 返回:包装后的对象函数
- 源码实现
function bindActionCreators(actionCreators,dispatch){
if(typeof actionCreators === 'function'){
return bindActionCreator(actionCreators,dispatch);
}
const boundActionCreators = {};
for(let key in actionCreators){
if(actionCreators.hasOwnProperty(key)){
boundActionCreators[key] = bindActionCreator(actionCreators[key],dispatch);
}
}
return boundActionCreators;
}
fucntion bindActionCreator(actionCreator,dispatch){
return function(){
return dispatch(actionCreator.apply(this,arguments));
}
}
combineReducers
- 参数:
reducers
对象 - 返回:
reducers
函数 - 源码实现
function combineReducers(reducers){
const reducerKeys = Object.keys(reducers);
return function(state={},action){
const nextState = {};
for(let i = 0;i<reducerKeys.length;i++){
const reducer = reducers[key];
const previousStateForkey = state[key];
const nextStateForkey = reducer(previousStateForkey,action);
nextState[key] = nextStateForkey;
}
return nextState;
}
}