【瞎寫程式碼】系列之redux表面理解
最近在翻boss,感覺今年前端的風向標的確是react,火爆程度完全超過了其他框架,所以為了跟上潮流,不得不開始使用react了,既然用了react就免不了要研究一下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程式碼,應該還比較好理解的把。感覺能看的下去的話,麻煩點個贊哈。。