20行代码实现redux,50行代码实现react-redux

timg (1).jpeg

redux的简陋版实现

简单实现了下redux,帮助理解redux的原理:

// 维持应用的 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方法,代码没几行,应该能看懂吧。

如何使用?


// 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的中间件并没有实现。

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>)

使用

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

问题是,<Container/>里面绑定了<Counter/>
而且还需要把<Consumer>通过props传到<Container/>里。
并且 展示组件里也只能获取一个状态counter

改进版

实现了connect函数,用法和 react-redux基本一样,代码如下:
connect.js

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:

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.js

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就好了。

源码

代码放在Github上了,欢迎star: https://github.com/cooleye/connect.js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容