实现 react-redux 简单管理状态数据功能

实现功能

Provider connect combineReducers createStore

思路

  1. createStore(reducer) 函数 提供getState() dispatch()
  2. combineReducers (reducers) 返回 一个 reducer(state={},action)
  3. Provider 返回一个 高阶组件 <Provider store={store} /> 利用 contxetstorre 传递给子组件
  4. connect (mapStateToProps,mapDispatchToProps)(UIComponent) 实现对组件 绑定 状态属性

context 使用

效果图
在这里插入图片描述

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {Button} from 'antd'
export default class ComponentA extends Component {
    constructor(props){
        super(props)
        this.state={
            colorToB:'red',
            colorToC:'pink',
            colorToD:'blue'
        }
    }
    // 申明向子组件中传递 contextTypes
    static childContextTypes = {
        colorToB: PropTypes.string,
        colorToC: PropTypes.string,
        colorToD: PropTypes.string,
    }
    // 向子组件传递 指定propTypes 的context 对象
    getChildContext(){
        return {
            colorToB:this.state.colorToB,
            colorToC:this.state.colorToC,
            colorToD:this.state.colorToD,
        }
    }
    render() {
        const {colorToB,colorToC} = this.state
        return (
            <div style={{background:'#333',padding:20,color:'white'}}>
                <p>父组件向子组件传递 context {`{colorToB:${colorToB},colorToC:${colorToC}}`}</p>
                <div style={{marginBottom:20}}>
                    <Button type='primary' onClick={()=> this.setState({colorToB:'green'})}>colorToB: green</Button>
                    <Button type='danger' onClick={()=> this.setState({colorToB:'red'})}>colorToB: red</Button>
                    <Button type='primary' onClick={()=> this.setState({colorToC:'green'})}>colorToC: green</Button>
                    <Button type='danger' onClick={()=> this.setState({colorToC:'red'})}>colorToC: red</Button>
                </div>
                <ComponentB></ComponentB>
            </div>
        )
    }
}

class ComponentB extends Component{
    constructor(props,context){
        super(props)
        console.log(context)
    }
    // 申明接收 父组件  contextTypes
    static contextTypes={
        colorToB:PropTypes.string
    }
    render(){
        return (
            <div style={{background:this.context.colorToB,padding:20}}>
                <p>子组件B 接收到 A 传递的 context {`{colorToB:${this.context.colorToB}}`}</p>
                <ComponentC></ComponentC>
            </div>
        )
    }
}



class ComponentC extends Component {
    static contextTypes = {
        colorToC: PropTypes.string
    }
    render() {
        const {colorToC} = this.context
        return (
            <div style={{background:colorToC,padding:20}}>
                <p>嵌套子组件C 接收到 A 传递的 context {`{colorToC:${this.context.colorToC}}`}</p>
                <ComponentD></ComponentD>
            </div>
        )
    }
}

function ComponentD (){
        return (
            <div style={{background:'blue',padding:20}}>
                <p>嵌套子组件D </p>
            </div>
        )
}

react-redux 实现效果

在这里插入图片描述

核心功能函数 react-redux.js

// 实现 provider connect combineReducers createStore

import PropTypes from 'prop-types'
import React,{Component} from 'react'

// 合并多个 reducer 里的 state  {state1,state2}
export function combineReducers(reducers){
    // 返回一个 reducer() 
    return (state={},action) => {
        // 遍历 reducers 对象 返回合并的新 state
        let newState = Object.keys(reducers).reduce((totalState,key)=>{
            totalState[key] = reducers[key](state[key],action)
            return totalState
        },{})
        return newState
    }
}

export function createStore(reducer){
    // 初始化传入的reducer 得到 state
    let state = reducer({},{type:'@@init'})
    const listeners = []
    
    function getState(){
        return state
    }
    function dispatch (action) {
        // 得到新的 state
        const newState = reducer(state,action)
        // 替换state
        state = newState
        // 执行监听
        listeners.forEach( listener => listener())
    }
    function subscritbe (listener){
        listeners.push(listener)
    }
    return {
        getState,
        dispatch,
        subscritbe
    }
}
export  class Provider extends Component {
    // 申明接收一个 store 对象
    static propTypes = {
        store:PropTypes.object.isRequired
    }
    // 申明向子节点传递 context
    static childContextTypes = {
        store:PropTypes.object
    }
    // 向子节点传递包含store的 context 
    getChildContext(){
        return {store:this.props.store}
    }
    render(){
        // 返回 provider 里 的子节点
        return this.props.children
    }
}

export function connect(mapStateToProps,mapDispatchToProps){
    // 返回一个函数    方法对传入的 ui 组件 包装并绑定 mapStateToProps,mapDispatchToProps 后返回 
    return (UiComponent) => {
        return class wrapperComponent extends Component{
            // 申明接收 store
            static contextTypes = {
                store:PropTypes.object
            }
            constructor(props,context){
                super(props)
                const {store} = context
                console.log(store)
                //数据属性 从store 里读取 需要的 state 保存到 wrapperComonent 状态响应 state 里 
                this.state = mapStateToProps(store.getState()) 
                // 方法属性
                if( typeof mapDispatchToProps === 'function'){
                    // 当 传入 mapDispatchToProps (dispatch) => {
                    //     return {
                    //         xxx: ()=> dispatch({})
                    //     }
                    // }
                    this.actions = mapDispatchToProps(store.dispatch)
                }else{
                    // 当传入 mapDispatchToProps{
                    //     xxx:xxx
                    // }
                    this.actions = Object.keys(mapDispatchToProps).reduce((actions,key)=>{
                        //mapDispatchToProps[key](...arg) 将返回函数 (dispatch)=> dispatch({type:xxx})
                        actions[key] = (...arg) => mapDispatchToProps[key](...arg)(store.dispatch)
                        return actions
                    },{})
                    // console.log(this.actions)
                }
                // 监听store state改变后 对wrapperComonent state做相应改变 从而改变 UICompoent 状态
                store.subscritbe(()=> {
                    this.setState({...mapStateToProps(store.getState())})
                })
            }
            render(){
                //对ui组件 绑定 mapStateToProps,mapDispatchToProps 
                return (
                    <UiComponent {...this.state} {...this.actions}></UiComponent>
                )
            }
        }
    }
}

测试使用

index.jsx

import React, { Component } from 'react'
import {Provider} from '../学习/react-redux'
import {store} from './store.js'
import TestRedux from './testredux'
export default class ReactRedux extends Component {
    render() {
        return (
            <Provider store={store}>
                <TestRedux></TestRedux>
            </Provider>
        )
    }
}

store.js

import {combineReducers,createStore} from '../学习/react-redux'
const ADD_COUNT = 'add_count'
const LOG = 'log'
// 导出 action
export const addCount = (step=1) => {
   return dispatch => {
        dispatch({type:ADD_COUNT,step})
        dispatch({
            type:LOG,
            log:JSON.stringify({type:ADD_COUNT,step}),
            date: Date.now()
        })
    }
} 

// 计数 reducer
function count (state = 0, action) {
    switch (action.type) {
        case ADD_COUNT:
            console.log(action)
            return state+action.step
        default:
            return state
    }
}
// 日志 reducer
function log (state=[{date:1,log:'null'}],action){
    switch (action.type) {
        case LOG:
            return [{date:action.date,log:action.log},...state]
        default:
            return state
    }
}
// 合并reducers
const reducer = combineReducers({count,log})
// 导出store
export const store = createStore(reducer)

testRedux.jsx

import React, { Component } from 'react'
import {Button,Icon} from 'antd'
import {connect} from '../学习/react-redux'
import {addCount} from './store.js'

 class TestRedux extends Component {
    updateCount = (step) => {
        this.props.addCount(step)
    }
    formatDate(times){
        const t =  new Date(times)
        return `${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} -- ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`
    }
    render() {
        const {count,log} = this.props
        return (
            <div style={{background:'#ccc',padding:20}}>
                <p>测试使用自己实现的 react-redux</p> 
                <p>count:  {count}</p>
                <Button type='primary' onClick={()=> this.updateCount(2)}><Icon type='plus'></Icon></Button>               
                <Button type='primary' onClick={()=> this.updateCount(-2) }><Icon type='minus'></Icon></Button> 
                <div>
                    <p>日志打印</p>
                    { log.map( item => <p key={item.date}>{ this.formatDate(item.date)}---------{item.log}</p>)}
                </div>              
            </div>
        )
    }
}
// const mapDispatchToProps = (dispatch) => {
//     return {
//         addCount: () => dispatch({type:'add_count',step:1})
//     }
// }
export default connect(
    state=>({
        count: state.count,
        log: state.log
    }),
    {addCount}
)(TestRedux)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值