本文通过点击按钮实现count加减的简单案例,来实现状态管理。进一步学习了解 redux 、 react-redux 的的源码实现过程。
基本代码
- 入口文件---index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// import { Provider } from 'react-redux'
import { Provider } from './react-redux/ReactRedux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
- app组件
import Counter from './Counter'
function App() {
return (
<div className="App">
<Counter></Counter>
</div>
);
}
export default App;
- count.jsx文件
import React, { Component } from 'react';
// import { connect } from 'react-redux'
import { connect } from './react-redux/ReactRedux'
import actions from './store/action'
class Counter extends Component {
// 发送异步action
handleAdd = () => {
this.props.dispatch((dispatch) => {
setTimeout(() => {
dispatch({type: 'ADD'})
}, 1000)
})
}
render() {
console.log('this.props', this.props);
return (
<div>
Counter
{
this.props.state?<p>{this.props.state.number}</p>:null
}
{/* <p>{this.props.state.number}</p> */}
{/* 当mapDispatchToProps为对象形式的时候,直接调用action中的add方法(返回的是
{type: actionTypes.ADD}),react-redux内部会调用redux中的bindActionCreators方法
将action包装成dispatch(action)的形式 */}
<button onClick={this.props.add}>+</button>
<button onClick={() => this.props.dispatch(actions.add())}>+</button>
<button onClick={this.props.sendAdd}>+</button>
{/* 执行count-- */}
<button onClick={() => this.props.dispatch({type: 'SUB'})}>直接dispatch到reducer</button>
{/* 执行异步操作 */}
<button onClick={this.handleAdd}>异步+</button>
</div>
);
}
}
let mapStateToProps = (state) => ({ state })
let mapDispatchToProps = (dispatch) => ({
sendAdd: () => dispatch(actions.add()),
dispatch // 将dispatch暴露到this.props中
})
// mapDispatchToProps 写成是一个函数
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
// mapDispatchToProps 写成是对象的形式
// export default connect(mapStateToProps, {...actions})(Counter);
- action-types.js -- 用于定义action常量
export const ADD = 'ADD'
export const SUB = 'SUB'
- action.js
import * as actionTypes from './action-type'
const actions = {
add() {
return {
type: actionTypes.ADD
}
}
}
export default actions
- reducer.js
import * as actionTypes from './action-type'
const reducer = (state = {number: 0}, action) => {
switch (action.type) {
case actionTypes.ADD:
return { number: state.number + 1 }
case actionTypes.SUB:
return { number: state.number - 1 }
default:
return state
}
}
export default reducer
- store.js
// import { createStore, applyMiddleware } from 'redux'
import { createStore, applyMiddleware, logger, thunk } from '../redux/ReduxPage'
import reducer from './reducer'
const store = createStore(reducer)
export default store
实现redux源码
// createStore 接收两个参数
export function createStore(reducer, enhancer) {
let currentState = undefined
let currentListeners = []
// getState方法 返回currentState
function getState() {
return currentState
}
function dispatch(action) {
// reducer 函数接收 currentState 和 action, 返回最新的state
currentState = reducer(currentState, action)
// 监听函数是一个数组
currentListeners.map(listener => listener())
}
// 订阅 可以多次订阅
function subscribe(listener) {
// 每次订阅,把回调放入回调数组
currentListeners.push(listener)
}
// 开始调用dispatch发送action,目的为了返回reducer定义的初始state的值
dispatch({type: "@INIT/STATE"})
if(enhancer) {
// 借助中间件返回加强版的 dispatch,同时覆盖掉store中原来的dispatch.
// enhancer = applyMiddleware(thunk, logger)
return enhancer(createStore)(reducer)
}
return {
getState,
dispatch, // 经过中间件加强的dispatch
subscribe
}
}
// applyMiddleware(thunk, logger)(createStore)(reducer)
export function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args) // 基础版的store
let dispatch = store.dispatch
const middleApi = {
getState: store.getState,
dispatch
}
// middlewareChain:是将getState dispatch 注入到中间件中(thunk等)后中间件的数组
const middlewareChain = middlewares.map(middleware => middleware(middleApi))
console.log('middlewareChain', middlewareChain)
// 将多个中间件聚合成一个返回加强版的 dispatch
// dispatch 需要经过每个中间件的加强,返回最终版的dispatch,第一个中间件需要我们手动传入dispatch
dispatch = compose(...middlewareChain)(dispatch);
return {
...store,
dispatch
}
}
}
// 聚合函数当前函数的参数为上个函数返回的结果
function compose(...funcs) {
if(funcs.length === 0) {
return arg => arg;
}
if(funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// 简单实现 logger 中间件
export function logger() {
return dispatch => action => {
console.log(action.type + '执行了')
return dispatch(action)
}
}
// 简单实现thunk中间件
export function thunk({dispatch, getState}) {
return dispatch => action => {
// action可以是对象可以是回调可以是函数
if (typeof action === 'function') {
console.log('thunk', dispatch)
/* 因为在聚合 applyMiddleware 中调用的顺序是 logger thunk,
同时compose()的结果是当前函数的参数是上一个函数的返回结果,
所以dispatch是logger函数dispatch的结果。
action => {
console.log(action.type + '执行了');
return dispatch(action);
}
*/
return action(dispatch, getState)
}else{
return dispatch(action)
}
}
}
实现react-redux源码
import React, { Component } from 'react'
// import { bindActionCreators } from 'redux'
// 借助context,实现状态共享,创建上下文
const valueContext = React.createContext()
// 实现 connect 高阶组件
// connect(mapStateToProps, mapDispatchToProps)(Component)
export const connect = (
mapStateToProps = state => state,
mapDispatchToProps
) => WarpedComponent => {
return class extends Component {
static contextType = valueContext
constructor(props) {
super(props)
this.state = {
props: {}
}
}
componentDidMount() {
const { subscribe } = this.context;
this.update()
// 订阅
subscribe(() => {
this.update()
})
}
//
update = () => {
const { dispatch, getState } = this.context
// 获取当前store中的state
let stateProps = mapStateToProps(getState())
let dispatchProps;
if (typeof mapDispatchToProps === 'object') {
// 如果传入的是一个对象的形式,会调用 redux 中bindActionCreators将action包装成 一个 dispatch(action)的形式
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
}else if(typeof mapDispatchToProps === 'function') {
// 如果是一个函数直接返回mapDispatchToProps函数执行返回的结果
/*
let mapDispatchToProps = (dispatch, ownPEops) => ({
sendAdd: () => dispatch(actions.add()),
dispatch
})
*/
// this.props 是 mapDispatchToProps 的第二参数 ownProps
dispatchProps = mapDispatchToProps(dispatch, this.props)
}else{
// 默认,就是在connect中不传入第二个参数
dispatchProps = {dispatch}
}
this.setState({
props: {
...stateProps,
...dispatchProps
}
})
}
render() {
console.log('context', this.context)
// 注入共享的state和dispatch,返回高阶组件
return (
<WarpedComponent {...this.state.props}/>
)
}
}
}
// 实现Provider
export class Provider extends Component {
render() {
return <valueContext.Provider value={this.props.store}>{this.props.children}</valueContext.Provider>
}
}
// 当mapDispatchToProps是一个对象的时候,{...action} ===> {add: ()=>({type: actionTypes.ASYNC_ADD })}
// 当在render 函数中直接调用 this.props.add() 是就会自动调用dispatch发送action
function bindActionCreator(creator, dispatch) {
// creator ---> (...args)=>({type: actionTypes.ASYNC_ADD }) -- actionCreator中定义的add函数
// dispatch 发送的action中包含type还有可能包含其他参数比如 payload
return (...args) => dispatch(creator(...args))
}
// 将action中的多个action对象包装成 dispatch(action)类型
export function bindActionCreators(creators, dispatch) {
const obj = {}
for (const key in creators) {
obj[key] = bindActionCreator(creators[key], dispatch)
}
return obj
}
react-redux理解:
当我们使用connect(mapStateToProps, mapDispatchToProps)(ChildComponent)时,
-
定义用来获取state的函数mapStateToProps和用来获取dispatch方法的mapDispatchToProps。
-
react-redux内部会调用mapStateToProps和mapDispatchToProps函数,并将state和dispatch方法传入到两个函数中,最后获取到两个函数的返回值(即state,dispatch),最最后将state、dispatch以父子组件传值的形式注入到ChildComponent的高阶组件中。 -
在connect()()返回的高阶组件中就可以获取到共享的state状态和dispatch方法。