【瞎寫程式碼】系列之redux表面理解

腳踩空了_做了前端發表於2019-04-09

最近在翻boss,感覺今年前端的風向標的確是react,火爆程度完全超過了其他框架,所以為了跟上潮流,不得不開始使用react了,既然用了react就免不了要研究一下redux。關於redux的使用場景,就看自己得業務需要了,就不多說了。這裡就簡單描述一下redux的使用原理和一些核心的結構,主要針對一些還沒有理解redux工作流的童鞋。 先看一下redux的流程圖

【瞎寫程式碼】系列之redux表面理解
這是一個非常非常簡單的工作流程圖,通過互動觸發action,這時候會建立一個action物件,然後將這個物件丟給reducers,reducers會根據這個action的行為,對其攜帶的資料進行處理,然後merge到state,這裡就需要一個方法來完成這個過程,這個就是dispatch,state發生變化後,觸發監聽(這個應該算是個釋出訂閱)。這應該算是一個比較好理解的工具了,過程並不複雜,這個過程中reducer的邏輯是注入進來的,所以這個方法傳進來就好。。好,這樣我們就知道redux的核心結構是什麼樣的了:

class MyRedux {
  constructor(reducer, defaultState) {
    this.reducer = reducer;
    this.subscribeIndex = 0;
    this.state = defaultState
      ? { ...defaultState }
      : this.reducer(undefined, {
          type: "qweqwe"
        });
    this.subscribeMap = {};
  }
  subscribe = func => {
    let indexKey = this.subscribeIndex++;
    this.subscribeMap[indexKey] = func;
    return () => {
      delete this.subscribeMap[indexKey];
    };
  };
  getState = () => {
    return this.state;
  };
  dispatch = action => {
    this.state = Object.assign(this.state, this.reducer(this.state, action));
      Object.keys(this.subscribeMap).forEach(key => {
      let subscribeFunc = this.subscribeMap[key];
          subscribeFunc && subscribeFunc();
    });
  };
}
複製程式碼

大概解釋一下,constructor裡面就是接受reducers和初始化state,這裡的'qweqwe'是隨便寫的,原始碼中用的是'@@redux/INIT',subscribe就是註冊監聽,dispatch就是接受action然後通過reducers改變state,接著就觸發註冊的監聽。 應該很好理解吧,再看一下redux其他的一些方法。

export function CreateStore(reducer) {
  return new MyRedux(reducer);
}
export function combineReducers(reducerMap) {
  return function(state = {}, action) {
    const nextState = {};
    Object.keys(reducerMap).forEach(key => {
      const reducer = reducerMap[key];
      nextState[key] = reducer(state[key], action);
    });
    return nextState;
  };
}
複製程式碼

這兩個方法就比較簡單了,建立store和合並reducers。 接下來就是provider,這個其實只要理解了context就好辦了,來我們直接看程式碼:

const MyReactContext = React.createContext();

export class MyProvider extends Component {
  static propTypes = {
    store: PropTypes.instanceOf(MyRedux),
    children: PropTypes.node
  };
  render() {
    const MyReactProvider = MyReactContext.Provider;
    return (
      <MyReactProvider value={this.props.store}>
        {this.props.children}
      </MyReactProvider>
    );
  }
}
複製程式碼

這是一個react節點,直接建立一個context,然後把子節點扔在provider裡面就行了,這樣就能在這個裡面通過consumer獲取到store了。最後就是connect了,這個對一些初學者來說還是有些繞的,使用的時候需要傳入兩次引數,一次是需要併入props的一些引數,一個是當前的元件,這塊是柯里化一個很好的應用,個人感覺柯里化的好處就是能把引數分類,切換頻次低的可以複用,不過用的還是比較少,理解太到位。接著說connect,這個是高階元件的用法,我們在外層函式傳入併入的引數,然後返回一個函式,這個函式接受一個react元件,返回一個新的包裝過後的元件。來看一下程式碼:

export function MyConnect(mapStateToProps, mapDispatchToProps) {
  function connectHOC(RawComponent) {
    return class NewComponent extends Component {
      unsubscribe = null;

      constructor(props) {
        super(props);
        this.state = {
        };
      }

      setSubscribe(store) {
        if (!this.unsubscribe) {
          this.unsubscribe = store.subscribe(() => {
            this.setState(prevState => {
                return {
                };
            });
          });
        }
      }

      componentWillUnmount() {
        this.unsubscribe && this.unsubscribe();
      }

      getState = store => {
        if (!mapStateToProps) {
          return store.getState();
        } else {
          return mapStateToProps(store.getState(), this.props);
        }
      };

      getDispatch = store => {
        if (!mapDispatchToProps) {
          return {
            dispatch: store.dispatch
          };
        } else {
          return mapDispatchToProps(store.dispatch, this.props);
        }
      };

      render() {
        const MyReactConsumer = MyReactContext.Consumer;
        return (
          <MyReactConsumer>
            {store => {
                this.setSubscribe(store);
                let mixedProps = {
                  ...this.getState(store),
                  ...this.getDispatch(store),
                  ...this.props
               }
              return <RawComponent store={store} {...mixedProps} />;
            }}
          </MyReactConsumer>
        );
      }
    };
  }

  return connectHOC;
}
複製程式碼

這就是connect方法,通過高階元件返回一個被包裝起來的元件,這樣就能消費store了,還有在render 的時候,我們要把監聽事件註冊進來,這個方法目前只是用來重新渲染,這個地方用的是函式式的方法,因為setState是非同步的,不可信(這個地方只是為了重新渲染,不要在意沒有東西。。。)。不過個人認為這個地方可以做更多的事情,不過老師告訴我沒有實際場景就不要過度設計,屬於偽設計(受教了。。。),最後要記得刪除監聽。
以上就是簡易的redux程式碼,應該還比較好理解的把。感覺能看的下去的話,麻煩點個贊哈。。

相關文章