实现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;
default: return 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对象
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 + 1, 0)
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 + 1, 0)
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>
}