Redux基礎篇

前言

爲何有使用

隨着JavaScript單頁應用開發日趨複雜,管理不斷變化的state變得十分困難html

三大原則

  • 【一】單一數據流
    • 整個應用的 state被儲存在一棵object tree中,而且這個object tree只存在於惟一的store
  • 【二】只讀state
    • 惟一改變state的方法就是觸發action
  • 【三】使用reducer函數執行修改
    • 根據觸發的action來通知reducer函數來執行相應修改state動做

設計思想

  • 【一】將整個應用狀態存儲到到一個地方,稱爲store,裏面保存一棵狀態樹state tree
  • 【二】組件能夠派發dispatch行爲actionstore,而不是直接通知其它組件
  • 【三】其它組件能夠經過訂閱store中的state來刷新本身的視圖

基礎使用

本文以一個小計數器爲例子逐一講解redux

原生版

  • 【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;
      }
  }
複製代碼
相關文章
相關標籤/搜索