深刻理解Redux:簡單實現一個Redux以及React-Redux

前言

redux相信都不陌生(陌生的看中文文檔:www.redux.org.cn/),它是 JavaScript 狀態容器,提供可預測化的狀態管理,你能夠在React, Vue, Angular,服務端等地方使用。redux由三部分組成:Action, Reducer, Store,你須要編寫的是Action和Reducer,好比最簡單的計數器例子css

Action 部分react

// action type
const INCREMENT = "increment";
const DECREMENT = "decrement";

// action creator 也叫action 建立函數
function increment() {
    return {
        type: INCREMENT
    }
}

function decrement() {
    return {
        type: DECREMENT
    }
}
複製代碼

Reducer 部分git

const initialState = {
    count: 0
};

function countReducer(state = initialState, action) {
    switch (action.type) {
        case INCREMENT: {
            return {
                count: state.count + 1
            }
        }
        case DECREMENT: {
            return {
                count: state.count - 1
            }
        }
        default: {
            return state
        }
    }
}複製代碼

Store 部分github

const store = createStore(countReducer);

// 設置監聽
store.subscribe(() => {
    console.log(store.getState().count);
});
// 設置時間間隔去觸發動做
setInterval(() => {
    store.dispatch(increment());
}, 1000);複製代碼

一個簡單的redux應用就由上面三部分組成,重點關注createStore方法,由於算是它結合了Action和Reducer,並且也算是redux的核心,因此咱們嘗試編寫一個簡單的createStore函數typescript

Redux的createStore的簡單實現

redux的createStore函數接收reducer,initialState兩個參數,(middleware暫時先不考慮),而後返回一個對象,以下所示redux

{
   getState () {},
   dispatch (action) {},
   subscribe (newListener) {},
   replaceReducer (newReducer) {}
}複製代碼

因此咱們很容易的就編寫了如下createStore的簡單實現代碼瀏覽器

JavaScript架構

function createStore (reducer, initialState = reducer(void 0, { type: null })) {
    let state = initialState; // 存儲state
    let currentReducer = reducer; // 存儲當前的reducer
    let listener = []; // 存儲監聽函數

    return {
        getState () {
            return state
        },
        dispatch (action) {
            state = currentReducer(state, action); // reducer用在這了
            listener.forEach(cb => cb()); // 核心,每次調用dispatch時都調用監聽的函數
            return action;
        },
        subscribe (newListener) {
            listener.push(newListener);
        },
        replaceReducer (newReducer) {
            currentReducer = newReducer
        }
    }
}複製代碼

typescript版app

export type Action = {
    type: string,
    [props: string]: any
};
export type ReducerType = (state: any, action: Action) => any;
export type Dispatch<T = Action> = (action: T) => any;
export type Store<T = any, E = Action> = {
    getState: () => T,
    dispatch: Dispatch<E>,
    subscribe: (listener: Function) => void,
    replaceReducer: (reducer: ReducerType) => void
};

export function createStore (reducer: ReducerType, initialState = reducer(void 0, { type: "" })): Store {
    let state = initialState;
    let currentReducer = reducer;
    let listener: Array<Function> = [];

    return {
        getState () {
            return state
        },
        dispatch (action: Action) {
            state = currentReducer(state, action);
            listener.forEach(cb => cb());
            return action;
        },
        subscribe (newListener: Function) {
            listener.push(newListener);
        },
        replaceReducer (newReducer: ReducerType) {
            currentReducer = newReducer
        }
    }
}複製代碼

好了,一個簡易的createStore就完成了,固然跟redux的createStore仍是有必定差異的,好比沒有中間件(這個後期在實現吧),沒有異常處理等等dom

React-Redux的Provider實現

在react中,通常會使用React-Redux庫,而最經常使用的就是<Provider/>組件和conncet方法了,接下了將簡單實現Provider組件和conncet方法

基於typescript

import React from "react";
import { Store, Dispatch, Action } from "./createStore";

export interface ProviderProps {
    store: Store
}

const StoreContext = React.createContext({
    store: {}
});

// Provider組件實現,主要使用react 的Context
export class Provider extends React.Component<ProviderProps> {

    constructor (props: any) {
        super(props);
    }

    render (): React.ReactNode {
        let value = {
            store: this.props.store
        };
        // 利用Context 將store傳到子組件中
        return (
            <StoreContext.Provider value={ value }>
                { this.props.children }
            </StoreContext.Provider>
        )
    }
}

type mapStateType = (state: any) => any;
type mapDispatchType = (dispatch: (action: any) => any) => any;
// connect函數,其實就是高階組件,利用了屬性代理
export const connect = (mapStateToProps: mapStateType, mapDispatchToProps: mapDispatchType) =>
    (Component: React.ElementType) =>
        class NewComponent extends React.Component<any> {
            state = {};
            static contextType = StoreContext; // !!!

            constructor (props: any, context: any) {
                super(props);
            }

            componentDidMount (): void {
                let store = this.context.store as Store; // 從context中獲取store
                store.subscribe(this.update); // 主要是設置監聽器,當組件狀態更新,將反應回store,使store也更新
                this.update();
            }

            private update = () => {
                let store = this.context.store as Store;
                let state = store.getState();
                const filterState = mapStateToProps(state); // !!
                const actions = mapDispatchToProps(store.dispatch); // !!
                this.setState({ ...filterState, ...actions });
            };

            render (): React.ReactNode {
                return <Component { ...this.state }/>
            }
        };
複製代碼

結合咱們實現的createStore以及Provider組件,connect函數,咱們來實現一個計數器的小例子

代碼以下:

typescript

import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css";
import { createStore, Action, Dispatch, Store } from "./createStore";
import { Provider, connect } from "./Provider";

// Action
const INCREMENT = "increment";
const DECREMENT = "decrement";

function increment (): Action {
    return {
        type: INCREMENT
    }
}

function decrement (): Action {
    return {
        type: DECREMENT
    }
}

// Reducer
type StoreState = {
    value: number
};

function reducer (state: StoreState = { value: 0 }, action: Action) {
    switch (action.type) {
        case INCREMENT: {
            return {
                value: state.value + 1
            }
        }
        case DECREMENT: {
            return {
                value: state.value - 1
            }
        }
        default: {
            return state
        }
    }
}

// Store
let CountStore = createStore(reducer);


type CountProps = {
    count: number;
    onDecrement: () => void;
    onIncrement: () => void;
};

function Count (props: CountProps) {
    return (
        <div className="count-block">
            <button className="increment-button" onClick={ props.onIncrement }>+</button>
            <span className="text-block">{ props.count }</span>
            <button className="decrement-button" onClick={ props.onDecrement }>-</button>
        </div>
    )
}

const mapStateToProps = (state: any) => ({
    count: state.value
});
const mapDispatchToProps = (dispatch: Dispatch<Action>) => ({
    onDecrement: () => dispatch(decrement()),
    onIncrement: () => dispatch(increment())
});
const CountContainer: React.ElementType = connect(mapStateToProps, mapDispatchToProps)(Count);

type MyAppProps = {
    store: Store
};

class MyApp extends React.Component<MyAppProps> {
    constructor (props: MyAppProps) {
        super(props);
    }

    render () {
        return (
            <Provider store={ this.props.store }>
                <CountContainer/>
            </Provider>
        );
    }
}


const render = () => ReactDOM.render(
    <MyApp store={ CountStore }/>,
    document.getElementById('root') as HTMLElement
);
CountStore.subscribe(render);
render();
複製代碼

能夠在瀏覽器上看效果

count

總結

經過簡單實現redux的createStore,以及react-redux的Provider, connect,能對redux的架構更加熟悉,知其因此然,因此用纔會順心順手,固然上面只是一個簡單的實現,若是要用於開發的話,固然建議直接用redux和react-redux,最後:因爲代碼是用typescript寫的,因此轉成JavaScript的話將類型定義給去了就行了(或者參考這裏,建立一個typescript的React的項目: facebook.github.io/create-reac…)。

相關文章
相關標籤/搜索