理解 Redux 的最好方式,是本身寫一個

react-redux 是 React 生態中比較(若是不是最的話)流行的一種狀態管理方式,而它所依託的 redux,繼承了 flux 的衣鉢,而且引入了單一數據源、state 爲只讀、只能經過純函數修改三大原則。一個最簡單的 redux 應用示例:javascript

// https://cn.redux.js.org/
import { createStore } from 'redux'

/** * 這是一個 reducer,形式爲 (state, action) => state 的純函數。 * 描述了 action 如何把 state 轉變成下一個 state。 * * state 的形式取決於你,能夠是基本類型、數組、對象、 * 甚至是 Immutable.js 生成的數據結構。唯一的要點是 * 當 state 變化時須要返回全新的對象,而不是修改傳入的參數。 * * 下面例子使用 `switch` 語句和字符串來作判斷,但你能夠寫幫助類(helper) * 根據不一樣的約定(如方法映射)來判斷,只要適用你的項目便可。 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// 建立 Redux store 來存放應用的狀態。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter)

// 能夠手動訂閱更新,也能夠事件綁定到視圖層。
store.subscribe(() => console.log(store.getState()))

// 改變內部 state 唯一方法是 dispatch 一個 action。
// action 能夠被序列化,用日記記錄和儲存下來,後期還能夠以回放的方式執行
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
複製代碼

createStore 是 redux 中最基礎的 API,它的做用就是返回一個 store;而這個 store 有三個方法 —— dispatchsubscribegetState。其實從方法名上,咱們大體能夠推測出 redux 應用了訂閱發佈模式;該模式能夠直接套用下面的模板代碼:java

// 開始
// createStore 是一個閉包,維護了 currentState, listeners 私有性;
// 外層想要獲取 state,只能經過 getState;
// listeners 沒法從外層修改
function createStore(reducer, preloadedState) {
  // 每次調用 subscribe,就會向 listeners 數組 push 一個 listener;
  // 每次調用 dispatch,listeners 數組中的函數會依次執行;
  const listeners = [];
  let currentReducer = reducer;
  let currentState = preloadedState;

  function dispatch(action) {
    // 正如示例代碼中所展示的,reducer(即 counter) 函數接受 state,action 兩個參數
    // action 是一個簡單對象,有一個 type 屬性;
    // reducer 中根據 type 執行對應操做,返回新的 state;
    currentState = currentReducer(currentState, action);
    // 執行 listeners 數組中的全部函數
    listeners.forEach(listener => listener());
  }

  function subscribe(listener) {
    listeners.push(listener);
    // 返回一個取消訂閱的函數
    return function unsubscribe() {
      let index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  // 這個函數的做用就是返回 state
  function getState() {
    return currentState;
  }

  return {
    dispatch,
    subscribe,
    getState
  };
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

// 測試一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
複製代碼

上面的代碼只是爲了大體解釋一下 redux 的運行機制,實際上是有很多問題的,好比 action 只能是一個簡單對象,但咱們的代碼中並無進行檢測;咱們暫且不去關注這些問題,而是先去實現功能。接下來,修改 dispatch,使它支持函數參數:react

function dispatch(action) {
  // 若是傳入一個函數,則爲這個函數注入 dispatch 和 getState 方法,
  // 而且執行這個 action
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  // 正如示例代碼中所展示的,reducer(即 counter) 函數接受 state,action 兩個參數
  // action 是一個簡單對象,有一個 type 屬性;
  // reducer 中根據 type 執行對應操做,返回新的 state;
  currentState = currentReducer(currentState, action);
  // 執行 listeners 數組中的全部函數
  listeners.forEach(listener => listener());
  return action;
}
複製代碼

用異步的方式,測試一下:redux

function counter(state = 0, action) {
 switch (action.type) {
   case 'INCREMENT':
     return state + 1;
   case 'DECREMENT':
     return state - 1;
   default:
     return state;
 }
}

function asyncAdd(dispatch) {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve(
       dispatch({
         type: 'INCREMENT'
       })
     );
   }, 1000);
 });
}
// 測試一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch(asyncAdd).then(() => store.dispatch(asyncAdd)); // 1s 後 3, 2s 後 4
複製代碼

如今,咱們的代碼已經具有了 redux + redux-thunk 的功能,並且沒有增長新的 API。但咱們是用入侵的方式修改了 dispatch,若是要增長其餘新功能怎麼辦?繼續修改 dispatch 麼?顯然不能這麼幹。redux 使用中間件的機制,來修改 dispatch:數組

function compose(...funcs) {
  if (funcs.length === 0) {
    return args => args;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = () => {
      throw new Error('not valid');
    };

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    };

    const chain = middlewares.map(middleware => middleware(middlewareAPI));
    // applyMiddleware 本質上,仍是須要去覆蓋原來的 dispatch 方法
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch
    };
  };
}

// 咱們的 thunk
const thunk = ({ dispatch, getState }) => next => action => {

  // 這段代碼,和咱們上面暴力修改 dispath 的方法,實際上是同樣的
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }

  return next(action);
};
複製代碼

以上就是 redux 以及 redux-thunk 的簡單實現,有很多 bug,但願對你們理解 redux 有點幫助;也建議你們去看看 redux 的源碼,其實源碼也很短,而且註釋很完整。數據結構

相關文章
相關標籤/搜索