This article uses a simple case of adding and subtracting count by clicking a button to implement state management. Learn more about redux, react-Redux source code implementation process.

Basic code

  1. Entry file –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'));Copy the code
  1. App component
import Counter from './Counter'

function App() {
  return (
    <div className="App">
      <Counter></Counter>
    </div>
  );
}
export default App;
Copy the code
  1. Count. JSX file
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 {
    // Send an asynchronous 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>} {/* When mapDispatchToProps is an object, call the add method in the action (which returns {type: Actiontypes.add}), the react-Redux will internally call the bindActionCreators method in redux to package the action as a Dispatch (action).<button onClick={this.props.add}>+</button>
                
                <button onClick={()= > this.props.dispatch(actions.add())}>+</button>

                <button onClick={this.props.sendAdd}>+</button>{/* execute count-- */}<button onClick={()= >This.props. Dispatch ({type: 'SUB'})}> Direct dispatch to the reducer</button>{/* Perform asynchronous operation */}<button onClick={this.handleAdd}>Asynchronous +</button>
            </div>); }}let mapStateToProps = (state) = > ({ state })

let mapDispatchToProps = (dispatch) = > ({
    sendAdd: () = > dispatch(actions.add()),
    dispatch  // Expose the dispatch to this.props
})

// mapDispatchToProps is written as a function
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

// mapDispatchToProps is written as an object
// export default connect(mapStateToProps, {... actions})(Counter);
Copy the code
  1. Action-types.js — Used to define action constants
export const ADD = 'ADD'
export const SUB = 'SUB'
Copy the code
  1. action.js
import * as actionTypes from './action-type'
const actions = {
    add() {
        return {
            type: actionTypes.ADD
        }
    }
}
export default actions
Copy the code
  1. 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
Copy the code
  1. 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
Copy the code

Redux source code implementation

// createStore receives two parameters
export function createStore(reducer, enhancer) {
    
    let currentState = undefined
    let currentListeners = []
    The getState method returns currentState
    function getState() {
        return currentState

    }

    function dispatch(action) {
       The Reducer function receives currentState and Action and returns the latest state
        currentState = reducer(currentState, action)
        // The listener is an array
        currentListeners.map(listener= > listener())
    }

    // Subscriptions can be subscribed multiple times
    function subscribe(listener) {
        // Each time you subscribe, put the callback into the callback array
        currentListeners.push(listener)
    }

    // Start calling Dispatch to send action in order to return the initial state defined by the reducer
    dispatch({type: "@INIT/STATE"})
    
    if(enhancer) {
        // Use middleware to return the enhanced dispatch, overwriting the original dispatch in the store.
        // enhancer = applyMiddleware(thunk, logger)
        return enhancer(createStore)(reducer)
    }
    return {
        getState,
        dispatch,  // Middleware enhanced dispatch
        subscribe
    }
}

// applyMiddleware(thunk, logger)(createStore)(reducer)
export function applyMiddleware(. middlewares) {
    return createStore= > (. args) = > {
        conststore = createStore(... args)// Base store
        let dispatch = store.dispatch
        const middleApi = {
            getState: store.getState,
            dispatch
        }
        // middlewareChain: is an array of middleware after injecting getState Dispatch into middleware (thunk, etc.)
        const middlewareChain =  middlewares.map(middleware= > middleware(middleApi))
        console.log('middlewareChain', middlewareChain)
        // Aggregate multiple middleware pieces into one dispatch that returns an enhanced version
        // The dispatch needs to be strengthened by each middleware to return the final version of the dispatch. The first middleware requires us to pass in the dispatch manuallydispatch = compose(... middlewareChain)(dispatch);return {
            ...store,
            dispatch
        }
    }
}

The argument to the current function is the result returned by the previous function
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))) }// Simply implement logger middleware
export function logger() {
    return dispatch= > action= > {
        console.log(action.type + 'Executed')
        return dispatch(action)
    }
}

// Simply implement thunk middleware
export function thunk({dispatch, getState}) {
    return dispatch= > action= > {
        // Action can be an object or a callback or a function
        if (typeof action === 'function') {
            console.log('thunk', dispatch)
            /* Dispatch is the result of logger dispatch because the order of calls in aggregate applyMiddleware is Logger thunk and the result of compose() is that the argument to the current function is the return of the previous function. Action => {console.log(action.type + 'executed '); return dispatch(action); } * /
            return action(dispatch, getState)
        }else{  
            return dispatch(action)
        }
    }
}
Copy the code

Implement react-redux source code

import React, { Component } from 'react'
// import { bindActionCreators } from 'redux'

// Use context to implement state sharing and create context
const valueContext = React.createContext()

// Implement connect higher-order components
// 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
           subscribe(() = > {
               this.update()
           })
        }
        // 
        update = () = > {
            const { dispatch, getState } = this.context
            // Get the state of the current store
            let stateProps = mapStateToProps(getState())
            let dispatchProps;
            if (typeof mapDispatchToProps === 'object') {
                // If the action is passed in as an object, the bindActionCreators in Redux is called to package the action as a Dispatch (action)
                dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
            }else if(typeof mapDispatchToProps === 'function') {
                // If it is a function, it returns the result returned by the execution of mapDispatchToProps
                /* let mapDispatchToProps = (dispatch, ownPEops) => ({ sendAdd: () => dispatch(actions.add()), dispatch }) */
                // this. Props is the second parameter of mapDispatchToProps
                dispatchProps = mapDispatchToProps(dispatch, this.props)
            }else{
                // By default, no second argument is passed in connect
                dispatchProps = {dispatch}
            }
            this.setState({
                props: {... stateProps, ... dispatchProps } }) }render() {
            console.log('context'.this.context)
            // Inject shared state and dispatch to return higher-order components
            return (
                <WarpedComponent {. this.state.props} / >)}}}/ / implementation of the Provider
export class Provider extends Component {
    render() {
        return <valueContext.Provider value={this.props.store}>{this.props.children}</valueContext.Provider>}}// When mapDispatchToProps is an object, {... action} ===> {add: ()=>({type: actionTypes.ASYNC_ADD })}
// Dispatch is automatically called when we call this.props. Add () directly in the render function to send the action

function bindActionCreator(creator, dispatch) {
    // creator ---> (... Args)=>({type: actiontypes.async_add}) -- Add function defined in actionCreator
    // The action sent by dispatch contains type and possibly other parameters such as payload
    return (. args) = >dispatch(creator(... args)) }// Wrap action objects in action as dispatches (action) type
export function bindActionCreators(creators, dispatch) { 
    const obj = {}
    for (const key in creators) {
        obj[key] = bindActionCreator(creators[key], dispatch)
    }
    return obj
}
Copy the code

The react – story to understand:

When we use connect(mapStateToProps, mapDispatchToProps)(ChildComponent),

  1. Define the function mapStateToProps for getting state and mapDispatchToProps for getting the dispatch method.

  2. React-redux internally calls mapStateToProps and mapDispatchToProps, passing in the state and dispatch methods to both functions, and finally getting the return values of both functions (state, dispatch). Finally, state and Dispatch are injected into the higher-order components of ChildComponent as parent-child values.

  3. The shared state and dispatch methods are available in the high-level component returned by connect()().