Use, principle explanation and manual implementation method of react Redux API

Application and understanding of react Redux

In normal times, we directly use Redux. We need to introduce a store into each page, execute getState() to obtain values, and subscribe and unsubscribe each time. Inconvenient to maintain

import React,{Component} from 'react'
import store from '../store'
export default class ReactRedux extends Component{
  constructor(){
    super()
  }
  componentDidMount(){// mount 
    this.unsubscribe=store.subscribe(()=>{
      this.forceUpdate()
    })
  }
  add=()=>{    
    store.dispatch({type:'ADD',payload:10})
  }
  componentWillUnmount(){// uninstall
    if(this.unsubscribe){
      this.unsubscribe()
    }
  }
  render(){
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{store.getState()}</p>
          <button
            onClick={this.add}
          >
            add
          </button>
        </div>
      </div>
    )
  }
}

Thus, the function of react Redux and Provider is introduced; Directly import the store under the root directory;

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import ReactRedux from './pages/ReactReduxPage'
import {Provider}  from 'react-redux'
import store from './store'

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

class components are introduced using connect

import {connect} from 'react-redux'

connect has three parameters: state, dispatch and mergeProps (merge props)

@connect(
  (state)=>({num:state})
)
class ReactRedux extends Component{
    render(){
      console.log(this.props)
      return(
        <div></div>      
      )
   }
}

@Connect is the use of decorators, or you can export default connect()(class...); The use of decorators can be checked by yourself without giving key explanations.

Print this Props

You can see the transmitted state value and the dispatch; We can get it in porps.

import React,{Component} from 'react'
import store from '../store/'
import {connect} from 'react-redux'
@connect(
  (state)=>({num:state})
)
class ReactRedux extends Component{
  constructor(){
    super()
  }
  add=()=>{    
    this.props.dispatch({type:'ADD',payload:10})
  }
  render(){
    const {num, dispatch} = this.props
    console.log(this.props,'this.props')
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{num}</p>
          <button
            onClick={this.add}
          >
            add
          </button>
        </div>
      </div>
    )
  }
}
export default ReactRedux

Directly update the status when clicking; You no longer need to call the life cycle. You can use the subscribe of redux to subscribe and update the status.

mapDispatchToProps of connect

dispatch can be an object or a function;

Object writing

@connect(
  //store
  (state)=>({num:state}),
  // dispatch type: obj|fn
  {
    add:()=>({
      type:'ADD',
      payload:20
    })
  }
)

Function writing

  dispatch=>{
    const add =()=>dispatch({ type:'ADD', payload:20})
    const minus=()=>dispatch({type:'MINUS', payload:10})
    return {dispatch,add ,minus}
  }

Or use import bindactioncreators from'redux'

Finally, the result of integration is the same as the above description, except that bindActionCreators is used for integration below

  dispatch=>{
    // const add =()=>dispatch({ type:'ADD', payload:20})
    // const minus=()=>dispatch({type:'MINUS', payload:10})
    //This is the same as the declaration annotation
    let creators={
      add:()=>({type:'ADD', payload:20}),
      minus:()=>({type:'MINUS', payload:10})
    }
    creators=bindActionCreators(creators, dispatch)
    return {dispatch,...creators}
  }

props will have add, minus and dispatch methods attached to it;

Use:

 Dispatchadd=()=>{
   this.props.dispatch({type:'ADD',payload:10})
 }

render(){
  const {num, add,minus} = this.props
  console.log(this.props,'this.props')
  return(
    <div>
      <h3>ReactRedux-page</h3>
      <div>
        <p>{num}</p>
        <button onClick={this.Dispatchadd}>Dispatchadd+10</button>
        <button onClick={add}>add+20</button>     
        <button onClick={minus}>minus-10</button>
      </div>
    </div>
  )

Summary of mapDispatchToProps
Writing method 1: function can directly return dispatch;

dispatch=>{
// Define other disatch methods in it
return (dispatch, other dispatch method)
}

Writing method 2: object directly defines dispatch, but in the process of building, you can only call the defined method name, not this Props Dispatch

 {
   add:()=>({
     type:'ADD',
     payload:20
   })
 }

import React,{Component} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
@connect(
  //Put a copy of state on props;
  `Parameter 1: mapStateToProps`
  (state)=>({num:state}),
  `Parameter 2: mapDispatchToProps`
  // Dispatch type: obj|fn place the dispatch on the poros; mapDispatchToProps
  // Dispatch writing method 1: it does not contain its own dispatch, and can only call the methods written in the object;
  // {
  //   add:()=>({
  //     type:'ADD',
  //     payload:20
  //   })
  // }
  // dispatch writing method 2:
  (dispatch,ownProps)=>{ 
  // ownProps means that when the incoming props are modified, they will be loaded every time; 
  //ownProps can not be written. Once written, this method will be called every time props is modified
  
    // Method 1: write this directly and export it
    // const add =()=>dispatch({ type:'ADD', payload:20})
    // const minus=()=>dispatch({type:'MINUS', payload:10})
    // return {dispatch, add, minus}
    // Method 2: use bindActionCreators to merge
    let creators={
      add:()=>({type:'ADD', payload:20}),
      minus:()=>({type:'MINUS', payload:10})
    }
    creators=bindActionCreators(creators, dispatch)
    return {dispatch,...creators}
  },
  `Parameter 3: mergeprops`,At the end of the explanation, it's not often used
  (stateProps,dispatchProps,ownProps)=>{
    return {
      ...stateProps,
      ...dispatchProps,
      ...ownProps
    }
  }
)
class ReactRedux extends Component{
  constructor(){
    super()
  }
  Dispatchadd=()=>{
    this.props.dispatch({type:'ADD',payload:10})
  }
  
  render(){
    const {num, add,minus} = this.props
    console.log(this.props,'this.props')
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{num}</p>
          <button onClick={this.Dispatchadd}>Dispatchadd+10</button>
          <button onClick={add}>add+20</button>     
          <button onClick={minus}>minus-10</button>       
        </div>
      </div>
    )
  }
}
export default ReactRedux

Instructions for mergeprops of connect
 (stateProps,dispatchProps,ownProps)=>{
   return {
     ...stateProps,
     ...dispatchProps,
     ...ownProps
   }
 }

Print this Value of props

dispatch and state

When the content is customized in return, the print result of props

 (stateProps,dispatchProps,ownProps)=>{
   return {
     ...stateProps,
     ...dispatchProps,
     ...ownProps,
     own:'Custom content'
   }
 }

If you delete stateProps or dispatchProps in return;
This Props will also be lost

In the function writing of mapDispatchToProps, a bindActionCreators is introduced to integrate dispatch. Now let's try to implement it ourselves.

Implement bindActionCreators

import {bindActionCreators} from 'redux'

This is the api of redux. bindActionCreators is used to combine one or more actions and dispatches to generate the content that mapDispatchToProps needs to generate.

function bindActionCreator(creator,dispatch){
// console.log(creator,'creator')
  return (...args) => {
    console.log(args,'...args') // The parameter passed in when the function is called by onClick
    dispatch(creator(...args))
  }
}

// Receive two parameters: creators and dispatch
export function bindActionCreators(creators, dispatch){
  console.log(creators,'creators')
  // Set an empty object
  let obj={} // Used to export
  // First, you need to traverse it and add a dispatch to each item;
  for(let key in creators){
    obj[key] = bindActionCreator(creators[key],dispatch)
  }
  console.log(obj,'obj')
  return obj
}

Description:

  • bindActionCreators exports an object, which is integrated with dispatch. So finally return obj
  • For In uses dispatch to package each object
  • Where does args come from?

    We now modify the add stack method:

       let creators={
         add:(num)=>({type:'ADD',...num}),
         minus:()=>({type:'MINUS', payload:10})
       }

    num is the parameter passed in when the method is called

    class ReactRedux extends Component{
     render(){
       const {num} = this.props
       <button onClick={()=>{add({payload:20})}}>add+20</button> 
     }

    At this time, let's print and see what agrs is now?

    It is the parameter passed in when we call the add method

Implement Provider

This is nothing b ut react Createcontext(); Nested one layer. The tone of voice is so awesome. Ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha

import React from  'react'

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

Implement commit

Build a shelf first
import React from  'react'

export const commit=(
  mapStateToProps=state=>state, //This is state
  mapDispatchToProps // This is the whole dispatch
)=>WrappedComponent=>props=>{ // Finally, a new build is returned by build +props
  return<WrappedComponent {...props}/>
}
Acquisition of stateProps
   // First, get the stateToProps passed in
   // The state is obtained from mapStStateToProps
    const stateProps = mapStateToProps(Here state From where) 
    //mapStateToProps is a function that passes in a state and exports a state;
How to obtain the state parameter of mapStateProps

First of all, our current build is a function build
How do function components get state? Get through getState, which is in the store; Combined with the context, we need to use useContext to obtain;

  // useContext reads the current Context;
  const store = useContext(Context);
  // Get the store from the store
  const {getState} =store
  const stateProps = mapStateToProps(getState())
  return<WrappedComponent {...props} {...stateProps}/>
Implement mapDispatchToProps

The initialization has been completed, and the implementation of the dispatch method is required
How to get a dispatch
Let's print out the store first

 // useContext reads the current Context;
 const store = useContext(Context);
 console.log('store Contents:', store)


Thus, it is known that the dispatch can be obtained from the store.
Next, consider how to update; The build is updated after the dispatch, so use the subscript to call forceUpdate to update the build
In function building, we use useReduce to implement the forceUpdate function, which is recorded on the official website.
Is there anything like forceUpdate?: https://zh-hans.reactjs.org/d...

There is another problem that needs to be solved. When we go to subscribe, we must cancel the subscription
In what life cycle do we invoke subscriptions?

useEffect or useLayoutEffect?


From the introduction, we can see that the call of useLayoutEffect is earlier than that of useEffect
useEffect has delay; If the component has subscriptions and updates in the constructor, the useEffect will be lost.
So we use useLayoutEffect

const {getState, dispatch, subscribe} =store

const dispatchProps={
    dispatch
  }
  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  useLayoutEffect(() => {  // Equivalent to componentDidMount; useLayoutEffect is executed earlier than useEffect
    // subscription
    const unsubscribe =subscribe(()=>{ 
      forceUpdate()// Refresh Status 
    })
    return () => { // Equivalent to componentWillUnmount
      // Unsubscribe
      if(unsubscribe){ 
        unsubscribe()
      }
    }
  }, [store]) // Triggered when the associated store changes
  
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
Implement mapDispatchToProps

  • From the above example, mapDispatchToProps can be displayed in two forms: object and function.
  • Let's print first to see what happens when mapDispatchToProps is a function?

Therefore, it can be concluded that if it is a function, you can directly cross the parameter dispatch and execute the mapDispatchToProps function

  • If mapDispatchToProps is an object, print the result

There is an object inside. We just need to encapsulate it with bindActionCreators and return it.

  let  dispatchProps={ // The definition becomes a let, and the following will be repeated planting according to mapDispatchToProps
    dispatch
  }
  // console.log('dispatchProps',dispatchProps);
  // console.log('mapDispatchToProps',mapDispatchToProps)
  // First, judge the type
   if (typeof mapDispatchToProps === 'function') {
     // If it is a function, it will pass in the diapatch, and then execute the function to return
      dispatchProps =mapDispatchToProps(dispatch)
   } else if (typeof mapDispatchToProps === 'object'){
     // If it is an object, call bindActionCreators to encapsulate the object and return
     dispatchProps =bindActionCreators(mapDispatchToProps,dispatch)
   }

Complete implementation code of react Redux

Path: src/myreactredux JS

import React,{useContext,useReducer,useLayoutEffect} from  'react'
import {bindActionCreators} from './MybindActionCreators'
const Context= React.createContext()
export const connect=(
  mapStateToProps=state=>state,
  mapDispatchToProps
)=>WrappedComponent=>props=>{
  
  // useContext reads the current Context;
  const store = useContext(Context);
  // Get the store from the store
  const {getState, dispatch, subscribe} =store

  // First, get the stateProps passed in
  // The state is obtained from mapStateToProps
  const stateProps = mapStateToProps(getState()) //mapStateToProps is a function that passes in a state and exports a state;

  let  dispatchProps={ // The definition becomes a let, and the following will be repeated planting according to mapDispatchToProps
    dispatch
  }
  // console.log('dispatchProps',dispatchProps);
  // console.log('mapDispatchToProps',mapDispatchToProps)
  // First, judge the type
   if (typeof mapDispatchToProps === 'function') {
     // If it is a function, it will pass in the diapatch, and then execute the function to return
      dispatchProps =mapDispatchToProps(dispatch)
   } else if (typeof mapDispatchToProps === 'object'){
     // If it is an object, call bindActionCreators to encapsulate the object and return
     dispatchProps =bindActionCreators(mapDispatchToProps,dispatch)
   }

  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  useLayoutEffect(() => {  // Equivalent to componentDidMount; useLayoutEffect is executed earlier than useEffect
    // subscription
    // console.log('useLayoutEffect')
    const unsubscribe =subscribe(()=>{ 
      forceUpdate()// Refresh Status 
    })
    return () => { // Equivalent to componentWillUnmount
      // Unsubscribe
      if(unsubscribe){ 
        unsubscribe()
      }
    }
  }, [store]) // Triggered when the associated store changes
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
}

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

Learning materials

github: https://github.com/speak44/le...
Branch: react Redux

Tags: redux

Posted by ec on Tue, 31 May 2022 02:12:56 +0530