React-redux Provider react-redux Provider react-redux Provider react-redux Provider react-redux Provider
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
React-redux: React-Redux: React-Redux: React-Redux: React-Redux: React-Redux: React-Redux: React-Redux Then go to the Internet to see other people’s analysis, read a lot of articles, most of them are only to a few key points, or a face mengfu. You can’t build a project and debug it. The following attached source code after understanding the analysis, are marked on the code annotations, will also explain. The react-Redux process should be familiar to those who read the source code. Let’s try our best to analyze each API in detail, as a share and as their own archive.
The Provider application
When using React-Redux, the first thing to use is a Provider. So let’s see how we can use it.
index.tsx
import AppContainer from './container/AppContainer'; import createStore from './store/createStore'; Const store = createStore(); // Create a Provider, Pass store to Provider as the first parameter to the Provider component reactdom.render (<AppContainer Store ={store} />, document.getelementById ('root'));Copy the code
AppContainer.tsx
render() {
const { store } = this.props;
return (
<Provider store={store}>
<Router>
<AppLayout />
</Router>
</Provider>
);
}
Copy the code
It is easy to see in the code that we use Provider, passing in store as the value of the current context, so that the component can retrieve the Redux store from the context.
store
Let’s move on to createstore.tx
import { applyMiddleware, compose, createStore } from 'redux'; import createSagaMiddleware from 'redux-saga'; import rootReducer from '.. /reducers/index'; import rootSaga from '.. /saga'; export default function configureStore(preloadedState? :any) {// createSagaMiddleware const sagaMiddleware = createSagaMiddleware(); const middleWares = [sagaMiddleware]; // middleWares passes applyMiddleware, extends store dispatch, and returns a function, Call createStore const middlewareEnhancer = applyMiddleware(... middleWares); RootReducer custom reducer // preloadedState Default state // wrapped enhancers // middlewareEnhancer parameters have values, CreateStore executes the middlewareEnhancer function. Return store and wrapped Dispatch const store = createStore(rootReducer, preloadedState, middlewareEnhancer); sagaMiddleware.run(rootSaga); return store; }Copy the code
The comments in the code roughly tell you what’s going on, so let’s go through it step by step. CreateSagaMiddleware first created the Redux-Saga middleware to extend Dispatch, so why extend Dispatch? The reason for this, just a little bit, is that we need to deal with asynchrony. In the original Redux, the Dispatch action had to be a plain object, and it had to be synchronous. However, our applications often have asynchronous operations such as timers, network requests, and so on. Extending Dispatch to accept functions that pass in the Dispatch itself as an argument is an asynchronous solution (Redux-thunk). For those interested, learn more about Redux and redux-Thunk/Redux-Saga. Moving on, we see that it passes the generated middleware to applyMiddleware, so what does this API do?
ApplyMiddleware and compose
From the name can be seen is the use of middleware, so how is it directly used? The argument is an array (more than one middleware). Let’s examine the code:
export default function applyMiddleware(... middlewares) { return createStore => (... // createStore const store = createStore(... Let dispatch = () => {throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) } const middlewareAPI = { getState: store.getState, dispatch: (... args) => dispatch(... Const chain = middlewares.map(Middleware => Middleware (middlewareAPI)) Dispatch = compose(... Chain)(store.dispatch) // Returns store, extended dispatch return {... store, dispatch } } }Copy the code
The key part of applyMiddleware is reassigning Dispatch after the compose function call. Let’s take a look at Compose
export default 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))) }Copy the code
The key code is in the last sentence, which basically loops through the function, as follows
const func = [f1, f2, f3]; compose(... func) //return f1(f2(f3(... Args))) // Note that the order of function calls is left to right, namely: F1 -> F2 -> F3Copy the code
So where are the stores and dispatches that applyMiddleware returns used? Leave the question and let’s move on to the basic details and we’re done with the analysis. Over here we go to the next line,
const store = createStore(rootReducer, preloadedState, middlewareEnhancer);
Copy the code
This line is clear: call createStore to generate store. Just to mention, if middlewareEnhancer is passed in, the generated store is the value returned by applyMiddleware. And that answers our question. Here we have finally analyzed the generation process of store and had a general understanding of store. Store is simply the built-in createStore functions SUBSCRIBE, getState, etc., and wrapped dispatch.
What does Provider do
So much said in front, finally get to the theme, ha ha. Ladies and gentlemen, don’t worry. Let’s take our time. Let’s start with the code,
import React, { useMemo } from 'react' import PropTypes from 'prop-types' import { ReactReduxContext } from './Context' import Subscription from '.. /utils/Subscription' import { useIsomorphicLayoutEffect } from '.. / utils/useIsomorphicLayoutEffect created by '/ / store for redux store function Provider ({store, context, Children}) {// define contextValue const contextValue = useMemo(() => {// Create a new class const subscription = new Subscription(store) // createListenerCollection notification method assigned to Subscription callback method, So the correction notification subscription. The onStateChange = subscription. NotifyNestedSubs return {store, subscription,}}, [store]) const previousState = useMemo(() => store.getState(), (store)) / / useIsomorphicLayoutEffect is useLayoutEffect or useEffect useLayoutEffect used here, To call before rendering useIsomorphicLayoutEffect (() = > {const {subscription} = contextValue / / subscribe to increase subscription subscription. TrySubscribe () OnStateChange executes if (previousState! . = = store getState ()) {subscription. NotifyNestedSubs ()} return () = > {/ / unloading subscription subscription. TryUnsubscribe () The subscription. The onStateChange = null}}, [contextValue previousState]) use ReactReduxContext / / by default, Is actually the React. CreateContext (null) const Context = Context | | ReactReduxContext / / return the React of the Context. The Provider, Redux is an extension of the React Provider Consumer app. You need to update the React Context to 16.3. React Context Return < context. Provider value={contextValue}>{children}</ context. Provider>} if (process.env.NODE_ENV ! == 'production') { Provider.propTypes = { store: PropTypes.shape({ subscribe: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired, getState: PropTypes.func.isRequired, }), context: PropTypes.object, children: PropTypes.any, } } export default ProviderCopy the code
We define contentValue, use the useMemo wrapper to listen to store changes, return store, and subscription.
Subscription
export default class Subscription { constructor(store, ParentSub) {// Initialize store this.store = store ParentSub = parentSub this.unsubscribe = null this.Listeners = nullListeners this.handlechangeWrapper = This. HandleChangeWrapper. Bind (this)} addNestedSub (the listener) {enclosing trySubscribe () / / be parentSub calls here, Return this.listeners. Subscribe (listener)} notifyNestedSubs() {// Listeners are also listeners to listeners Notice ()} handleChangeWrapper() {if (this.onstatechange) {// onStateChange will be instantiated externally as a subcription instance, Is assigned to different update functions, NotifyNestedSubs method this.listeners. Notify () this.onStatechange ()}} isdebt () { return Boolean(this.unsubscribe) } trySubscribe() { if (! // If parentSub is not passed, then use store to subscribe; otherwise, call subscrption from context to subscribe. Make sure you all subscribe to one place this.unsubscribe = this.parentSub? this.parentSub.addNestedSub(this.handleChangeWrapper) : Listeners = listeners = listeners = listeners = listeners = listeners = listeners = listeners = listeners = listeners tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null this.listeners.clear() this.listeners = nullListeners } } }Copy the code
Subscription associates page updates with changes in status. The trySubscribe method is used to subscribe to store or Subscription, depending on the situation. The listeners are added to the listeners array. When state changes, the listeners loop through each listener, triggering page updates. TrySubscribe determines whether to subscribe directly using store or addNestedSub according to different situations. In the Provider scenario, the listener is subscribed to the Store. The latter is to place checkForUpdates internally into an array of Listeners, and actually subscribe to the Subscrption instance passed by the Provider to ensure that all connected components are subscribed to one Subscrption instance.
conclusion
The basic analysis of the Provider is complete. The main function of the Provider is to get the store we pass in from props and deliver the store as one of the values of the context to the underlying components. Whenever a store changes, the Provider responds to ensure that the latest store is always put into the context. So how to inject state and Dispatch into the component, you need to analyze connect again.