20行代碼實現redux,50行代碼實現react-redux

redux的簡陋版實現

簡單實現了下redux,幫助理解redux的原理:css

// 維持應用的 state
// 提供 getState() 方法獲取 state
// 提供 dispatch(action) 方法更新 state
// 經過 subscribe(listener) 註冊監聽器
// 經過 subscribe(listener) 返回的函數註銷監聽器
// createStore參數爲可變參數,第一個參數爲reducer,第二個參數爲初始state
export function createStore(...arg){
    let state = null;
    let reducer = arg[0];
    // 使用第二個參數爲state初始化
    if(arg.length > 1){state = arg[1]}
    // 保存監聽器的數據
    let listeners = [];
    let getState = () => state;
    let subscribe = listener => listeners.push(listener)
    let dispatch = (action) =>{
        //執行reducer函數,更新狀態
        state = reducer(state,action)
        //遍歷listeners,執行之中的監聽器函數
        listeners.forEach(listener => listener())
    }
    return {
        getState,
        dispatch,
        subscribe
    }
}

實現了redux的createStore方法,代碼沒幾行,應該能看懂吧。react

如何使用?

// import {createStore} from 'redux'

import {createStore} from '../myredux'

import reducer from './reducer'

const initialState = {
    counter:0,
    title:'nihao'
}
const store = createStore(reducer,initialState)

export default store;

把導入redux的代碼換成myredux便可,其餘使用和redux同樣。固然,redux的中間件並無實現。git

react-redux簡陋版實現

react-redux實現思路
藉助於context,把store經過Provider實現共享,這樣,在Provider內部的子組件就能夠得到store,而後在內部組件,須要獲取狀態的地方,使用consumer包裝,得到store,就能夠實現狀態共享了。

版本一:

let Container = ({store}) => {

    let [counter,setCounter] = useState(0);
    
    useEffect(() =>{
        store.subscribe(() => {
            setCounter(store.getState().counter)
        });
    })

    let add = () =>{
        store.dispatch({
            type:"INCREASE",
            num:1
        })
    }
    let min = () =>{
        store.dispatch({
            type:"DECREASE",
            num:1
        })
    }
    return  <Counter  counter={counter}  min={min}  add={add}/>
}


export default ({Consumer}) => (
    <Consumer>
        { (store) => <Container store={store}/>}
    
    </Consumer>)

使用github

<Provider  store={store}>
    <Container  Consumer={Consumer}>
    </Container>
</Provider>

問題是,<Container/>裏面綁定了<Counter/>
並且還須要把<Consumer>經過props傳到<Container/>裏。
而且 展現組件裏也只能獲取一個狀態counterredux

改進版

實現了connect函數,用法和 react-redux基本同樣,代碼以下:
connect.jside

import React,{createContext} from 'react';
const {Provider,Consumer} = createContext();
export const Container = ({store,children}) => {
    return (
        <div>
            <Provider value={store}>
                {children}
            </Provider>
        </div>
    )
}
class Inner extends React.Component{  
    constructor(props){
        super(props)
        this.state = {}
        let {mapStateToProps,store} = this.props;
        //從mapStateToProps得到用戶須要的狀態
        let mapState = mapStateToProps(store.getState());
        for(let key in mapState){
            this.state[key] = mapState[key]
        }
    }
    componentDidMount(){
        let {store} = this.props
        //註冊監聽,這樣當state發生改變時,改變Inner的內部狀態,把這個新狀態在render中傳給了展現組件Comp,Comp就能夠實時獲取最新狀態了
        store.subscribe(()=>{
            let storeState = store.getState();
            for(let key  in this.state){
                this.setState({
                    [key]: storeState[key]
                })
            }
        })     
    }
    render() {
        let {store,Comp,mapDispatchToProps} = this.props;
        let actions = mapDispatchToProps(store.dispatch)
        //把狀態和方法傳入到展現組件中
        return (<Comp {...actions} {...this.state} />)
    }
}
//connnect是一個高階組價,返回一個函數,接受展現組件爲參數,使用<Consumer/>包裝,傳入 store
export const connect = (mapStateToProps,mapDispatchToProps) =>{
    return (Comp) => {
        return () => (
            <Consumer>
            { (store) =>( <Inner  Comp={Comp}  store={store} mapStateToProps={mapStateToProps} mapDispatchToProps={mapDispatchToProps}></Inner> ) }
        </Consumer>)
    }
}

如何使用:

使用方法,和react-redux基本上是如出一轍的,只不過把Provider換成了Container,不過我徹底能夠叫Provider,一個名稱而已。函數

在App.js:this

import React from 'react';
import './App.css';
import store  from './store'
import Cart from './components/Cart1'
import {Container} from './connect.js'

function App() {
    return (
        <Container  store={store}>
            <Cart/>
        </Container>
    );
}

export default App;

cart.jsspa

import React from 'react'
import {connect} from '../connect'

let Counter = ({counter,title,min,add,changeTitle}) =>{

    return (
        <div>
            <h1> {counter} </h1>
            <h2> { title }</h2>
            <button onClick={min}> - </button>
            <button onClick={add}> + </button>

            <button onClick={changeTitle}> update title </button>
        </div>)
}

const mapStateToProps = (state) => {
    return {
        counter: state.counter,
        title:state.title
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        add: () => {
            dispatch({type:"INCREASE",num:1})
        },
        min: () => {
            dispatch({type:"DECREASE",num:1})
        },
        changeTitle() {
            dispatch({type:"UPDATE_TITLE"})
        }
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(Counter);

已經能夠和react-redux 徹底同樣的用法。
固然,這裏的實現只是爲了幫助理解react-redux內部是如何實現,並不必定是最好用的,實際工做中直接使用 react-redux就行了。code

源碼

代碼放在Github上了,歡迎star: https://github.com/cooleye/co...

相關文章
相關標籤/搜索