React-redux summary

Time:2020-5-10

React-redux summary

1.Provider

function Provider({store, context, children}){
    //Contextvalue initialization
    const contextValue = useMemo(()=>{
        const subscription = new Subscription(store);
        subscription.onStateChange = subscription.notifyNestedSubs;
      //Custom subscription model for subcomponent subscription
        return {
            store,
            subscription
        }
    }, [store])
    const previosState = useMemo(()=> store.getState(),[store]);
    useEffect(()=>{
        const { subscription } = contextValue;
      //The root node subscribes to the original store, and the child node subscribes to the subscription in the parent component
        subscription.trySubscribe();
        if(previosState !== store.getState()){
          //The subscription model forms a mesh structure, which ensures that the execution order of the subscription function is from the parent node to the child node, and prevents the subscribers of the child node from triggering the expired props first.
            subscription.notifyNestedSubs();
        }
        return ()=>{
            subscription.tryUnsubscribe();
            subscription.onStateChange = null;
        }
    },[contextValue, previosState])
    const Context =context || ReactReduxContext;
    return <Context.Provider value={contextValue}>{children}</Context.Provider>
}

The above logic can be summarized as follows:

  • Create a new subscription model subscription for subcomponent subscription
  • Subscription call in useeffectsubscription.trySubscribe()Subscribe to the store from the parent component.
  • When the store triggers the subscription function due to the dispatch, execute thesubscription.notifyNestedSubsBecause the child component subscribes to the subscription of the parent component, the child component triggers the subscription function

    …………….

  • Pass the modified store into the sub components through the context API.

2. Connect

Connect can process the data and methods in the store and pass them to the package components through props.

2.1 mapStateToProps: (state, ownProps) => stateProps

Mapstatetoprops method is mainly used to reshape the data in the store. Because every store change triggers the mapstatetoprops function in all connections, the function should run fast enough to avoid affecting performance. When necessary, the selector library can be used to avoid unnecessary calculation (the last cached value is used instead of recalculation in the case of the same input).

React Redux is optimized to avoid unnecessary re rendering,

(state) => stateProps (state, ownProps) => stateProps
mapStateToPropsExecution conditions: store statechange store stateChange or ownprops change
Component re render conditions: Stateprops change State props change or ownprops change

From the above table, we can summarize some tips:

  • It is judged that the change of stateprops is compared by shallow equality checks. Even if the input value of stateprops is the same, if each field in stateprops returns a new object, it will trigger re rendering. You can use selector to cache the last value to avoid stateprops changes.
  • When the store state does not change,mapStateToPropsWill not be executed. After each dispatch, connect will call store. Getstate() to get the latest state and uselastState === currentStateJudge whether it changes. It is also optimized in the Redux combinedreducers API, that is, when the state in the reducer does not change, the original state is returned.
  • So is ownpropsmapStateToPropsThe conditions of execution and component re rendering, so do not transfer when it is not possible.

2.2 mapDispatchToProps

Mapdispatchtoprops canstore.dispatchOr dispatch an action method((…args) => dispatch(actionCreator(…args)))In the component that is delivered to its package. Mapdispatchtoprops can be used in a variety of ways:

  • When mapdispatchtoprops is not passed, the dispatch method is passed to its wrapped component.
  • When mapdispatchtoprops is defined as a function,(dispatch,ownProps)=>dispatchProps

    const increment = () => ({ type: 'INCREMENT' })
    const decrement = () => ({ type: 'DECREMENT' })
    const reset = () => ({ type: 'RESET' })
    
    const mapDispatchToProps = dispatch => {
      return {
        // dispatching actions returned by action creators
        increment: () => dispatch(increment()),
        decrement: () => dispatch(decrement()),
        reset: () => dispatch(reset())
      }
    }

    The bindactioncreators interface is provided in Redux to automatically convert actioncreators to corresponding dispatch methods:

    import { bindActionCreators } from 'redux'
    
    const increment = () => ({ type: 'INCREMENT' })
    const decrement = () => ({ type: 'DECREMENT' })
    const reset = () => ({ type: 'RESET' })
    
    function mapDispatchToProps(dispatch) {
      return bindActionCreators({ increment, decrement, reset }, dispatch)
    }
  • When mapdispatchtoprops is defined as action creators key value pair, bind action creators will be automatically called inside connect to convert it into Dispatching action function form(dispatch => bindActionCreators(mapDispatchToProps, dispatch))。

3. batch

By default, each dispatch will execute the connect function and the next possible repeated rendering process. Using the batch API, multiple dispatch can be merged, similar to the setstate merge process.

import { batch } from 'react-redux'

function myThunk() {
  return (dispatch, getState) => {
    // should only result in one combined re-render, not two
    batch(() => {
      dispatch(increment())
      dispatch(increment())
    })
  }
}

4. hooks

You can subscribe to a store or dispatch action without using the connect package component.

4.1 useSelector()

Here is a simple implementation of useselector:

const useSelector = selector => {
  const store = React.useContext(Context);
  const [, forceUpdate] = React.useReducer(c => c + 1, 0);
  const currentState = React.useRef();
  //Update the state in the re render phase so that the obtained props are not expired
  currentState.current = selector(store.getState());

  React.useEffect(() => {
    return store.subscribe(() => {
      try {
        const nextState = selector(store.getState());

        if (nextState === currentState.current) {
          // Bail out updates early
          return;
        }
      } catch (err) {
        // Ignore errors
        //Ignore calculation errors due to expired props
      }
            //State changes need to be re rendered
      //Either way we want to force a re render
      forceUpdate();
    });
  }, [store, forceUpdate, selector, currentState]);

  return currentState.current;
};

Comparison of old and new states in useselector===, instead of a shallow comparison in connect, so the selector returns a new object that causes each re rendering. To solve this problem, you can use multiple selectors to return basic type values, or use the reselect library to cache the calculation results. Finally, you can pass the second parameter of useselector to customize the comparison function between the old and new states.

Connect will compare the old and new state and props values to determine whether to execute mapstatetoprops, but there is no such mechanism in useselector, so it will not prevent the re render caused by the re render of the parent component (even if the props does not change). In this case, you can useReact.memoOptimization.

4.2 useDispatch()

Return a reference to the dispatch

const dispatch = useDispatch()

4.3 useStore()

Return reference to store

const store = useStore()