Loading...
墨滴

Phil

2021/05/24  阅读:38  主题:前端之巅同款

react-redux

实现react-redux

前置文章: 实现redux

关键api

  • Provider 组件

使组件层级中的 connect() 方法都能够获得 Redux store

原理是通过 react 中context这个api

  • connect(mapStateToProps, mapDispatchToProps)

连接 React 组件与 Redux store

作用: 返回一个新的已与 Redux store 连接的组件, 该组件props中包含mapStateToProps定义的state props,mapDispatchToProps中定义的dispatch props

用法

在index.js 中引入Provider, 提供一个全局的store

  • index.js
import React from 'react';
import ReactDOM from 'react-dom';
// import { Provider } from 'react-redux'
import { Provider } from './simple-react-redux'
import store from './store'
import './index.css';
import App from './App';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
,
  document.getElementById('root')
);

store 定义

  • store.js
import { createStore } from "redux";


const countReducer = (state = 0, action) => {
    switch (action.type) {
        case 'ADD'return state + 1;
        case 'MINUS'return state - 1;
        defaultreturn state
    }
}

const store = createStore(countReducer)

export default store

在子组件中使用store数据

通过mapStateToProps函数将store中的state重命名为num, 作为props

通过mapDispatchToProps函数将action相应的dispatch函数,作为props, 案例中的add 和 minus方法

  • ReactReduxPage.js
import React, { Component } from "react"
import { connect } from "react-redux"
// import { connect } from "./simple-react-redux"
class ReactReduxPage extends Component {
  render() {
    const { num, add, minus } = this.props
    return (
      <div>
        {" "}
        <h1>ReactReduxPage</h1> <p>{num}</p> <button onClick={add}>add</button>{" "}
        <button onClick={minus}>minus</button>{" "}
      </div>

    )
  }
}
const mapStateToProps = (state) => {
  return { num: state }
}
const mapDispatchToProps = {
  add() => {
    return { type"ADD" }
  },
  minus() => {
    return { type"MINUS" }
  },
}
export default connect(mapStateToProps, mapDispatchToProps)(ReactReduxPage)

实现simple-react-redux

Provider 组件 通过React.createContext创建一个上下文对象, 通过上下文对象的Provider这个api生成新Provider组件, 接受store对象

react Context使用

import React from 'react'

const Context = React.createContext();

export function Provider(props{
    const { store, children } = props
    return <Context.Provider value={store}>
        {children}
    </Context.Provider>

}

connect 接受mapStateToProps, 和mapDispatchToProps 两个参数, 返回一个高阶组件。

该高阶组件包含mapStateToProps, 和mapDispatchToProps所映射在store中的数据和dispatch方法。

通过useContext来获取到上下文对象,也即Provider中的store。 store中包含subscribe, dispatch, getState等方法, 在connect中会默认调用一次subscribe方法(初始化, 让页面绑定数据后刷新一下状态),这样就不用在组件中调用。

mapStateToProps 接受store中的state数据(即getState返回的数据) mapDispatchToProps 接受一个对象或者函数, 示例中使用对象的形式, 返回一个新的函数。

示例: () => dispatch({type: 'ADD'}) 这样我们在调用add方法时, 实际调用的就是store中 dispatch({type: 'ADD'})

export const connect = (
    mapStateToProps = state => state,
    mapDispatchToProps,
) => (WrappedComponent) => (props) => {
    const store = useContext(Context)
    const { subscribe, dispatch, getState } = store || {}
    const stateProps = mapStateToProps(getState())
    let dispatchProps = { dispatch }

    const [, forceUpdate] = useReducer(x => x + 10)
    
    if (typeof mapDispatchToProps === 'function') {
        dispatchProps = mapDispatchToProps(dispatch)
    } else if (typeof mapDispatchToProps === 'object') {
        dispatchProps =  bindActionCreators(mapDispatchToProps, dispatch)
    }

    useLayoutEffect(() => {
        const unsubscribe = subscribe(() => {
            forceUpdate()
        })

        return () => {
            if (unsubscribe) {
                unsubscribe()
            }
        }
    }, [store, subscribe])

    return <WrappedComponent  {...props} {...stateProps} {...dispatchProps} />
}

simple-react-redux 完整代码

  • simple-react-redux.js
import React, { useContext, useReducer, useLayoutEffect } from 'react'

const Context = React.createContext();

const bindActionCreator = (creator, dispatch) => {
    return (...args) => dispatch(creator(...args))
}

const bindActionCreators = (creators, dispatch) => {
    const obj = {};
    for ( let key in creators) {
        obj[key] = bindActionCreator(creators[key], dispatch);
    }
    return obj
}

export const connect = (
    mapStateToProps = state => state,
    mapDispatchToProps,
) => (WrappedComponent) => (props) => {
    const store = useContext(Context)
    const { subscribe, dispatch, getState } = store || {}
    const stateProps = mapStateToProps(getState())
    let dispatchProps = { dispatch }

    const [, forceUpdate] = useReducer(x => x + 10)
    
    if (typeof mapDispatchToProps === 'function') {
        dispatchProps = mapDispatchToProps(dispatch)
    } else if (typeof mapDispatchToProps === 'object') {
        dispatchProps =  bindActionCreators(mapDispatchToProps, dispatch)
    }

    useLayoutEffect(() => {
        const unsubscribe = subscribe(() => {
            forceUpdate()
        })

        return () => {
            if (unsubscribe) {
                unsubscribe()
            }
        }
    }, [store, subscribe])

    return <WrappedComponent  {...props} {...stateProps} {...dispatchProps} />
}

export function Provider(props{
    const { store, children } = props
    return <Context.Provider value={store}>
        {children}
    </Context.Provider>

}

案例完整代码

案例完成代码

Phil

2021/05/24  阅读:38  主题:前端之巅同款

作者介绍

Phil