Redux基础篇

602 阅读3分钟

前言

为什么有使用

随着JavaScript单页应用开发日趋复杂,管理不断变化的state变得十分困难

三大原则

  • 【一】单一数据流
    • 整个应用的 state被储存在一棵object tree中,并且这个object tree只存在于唯一的store
  • 【二】只读state
    • 唯一改变state的方法就是触发action
  • 【三】使用reducer函数执行修改
    • 根据触发的action来通知reducer函数来执行相应修改state动作

设计思想

  • 【一】将整个应用状态存储到到一个地方,称为store,里面保存一棵状态树state tree
  • 【二】组件可以派发dispatch行为actionstore,而不是直接通知其它组件
  • 【三】其它组件可以通过订阅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

  • 参数:actionCreatorsdispatch
  • 返回:包装后的对象函数
  • 源码实现
  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;
      }
  }