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 useeffect
subscription.trySubscribe()
Subscribe to the store from the parent component. - When the store triggers the subscription function due to the dispatch, execute the
subscription.notifyNestedSubs
Because 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 | |
---|---|---|
mapStateToProps Execution conditions: |
store state change |
store state Change 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,
mapStateToProps
Will not be executed. After each dispatch, connect will call store. Getstate() to get the latest state and uselastState === currentState
Judge 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 ownprops
mapStateToProps
The conditions of execution and component re rendering, so do not transfer when it is not possible.
2.2 mapDispatchToProps
Mapdispatchtoprops canstore.dispatch
Or 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.memo
Optimization.
4.2 useDispatch()
Return a reference to the dispatch
const dispatch = useDispatch()
4.3 useStore()
Return reference to store
const store = useStore()