preface

With regard to Redux, the following statements are made for ease of understanding

  • Redux is a state manager.
  • Redux has nothing to do with React; redux can be used in any framework.
  • Connect and react-Redux are not redux.
  • The proper nouns of redux, one by one
  1. store
  2. CreateStore (return dispatch, getState, SUBSCRIBE, replaceReducer)
  3. action
  4. reducer
  5. combineReducers
  6. middleware
  7. applyMiddleware

A, createStore

Writing in the front

Define store concepts (including but not limited to reading state, but also distributing Actions and subscribing to state changes)

* @returns {Store} A Redux store that lets you read the state, dispatch actions

* and subscribe to changes.

1. Internal overview

Take a look at the createStore structure, clear enough, without the source code comments and powerful exception handling.



So createStore is a function that creates a store object. The store contains methods like getState, Dispatch, subscribe, replaceReducer, and Observable.

2, getState

  function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }
Copy the code

That’s all the source code is, to get the current state

3, the subscribe

Listeners = [] listeners = listeners function subscribe(listener) (typeof listener ! == 'function') {throw new Error('Expected the listener to be a function.')} throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.')} let isSubscribed = true // Core Return function unsubscribe() {if (! isSubscribed) { return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.' ) } isSubscribed = false const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } }Copy the code

The nextListeners. Push line is an array of listeners that uses state, and the listeners are called at the dispatcher.

4, dispatch

let currentReducer = reducer let currentListeners = [] function dispatch(action) { try { isDispatching = true // CurrentState = currentReducer(currentState, } finally {isregularly = false} // The listeners are const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; Listeners ()} return action} listeners () {listeners = listeners[I]Copy the code

These are the core parts of dispatch source code. The function is to generate new states through the current reducer, and then inform the users of each state

5, replaceReducer

function replaceReducer(nextReducer) { if (typeof nextReducer ! == 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer // This action has a similiar effect to ActionTypes.INIT. // Any reducers that existed in both the new and old rootReducer // will receive the previous state. This effectively populates // the new state tree with any relevant data from the old one. dispatch({ type: ActionTypes.REPLACE }) }Copy the code

Redux provides a method to replace the current reducer, and there are few actual usage scenarios

6, observables

function observable() { const outerSubscribe = subscribe return { /** * The minimal observable subscription method. * @param {Object} observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns {subscription} An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */ subscribe(observer) { if (typeof observer ! == 'object' || observer === null) { throw new TypeError('Expected the observer to be an object.') } function observeState() { if (observer.next) { observer.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } }Copy the code

Reference reactivex. IO/RXJS/class /…

Second, the Reducer

1, concept,

Reducer: a reducer function that receives the old state and returns the new state according to the ‘type’ of the action (switch case)

Action: Action is an object and must contain the Type field

CombineReduces: Merge multiple reducer. State is returned

2, redux-Demo-async source code analysis

import { combineReducers } from 'redux'; import { INVALIDATE_SUBREDDIT, RECEIVE_POSTS, REQUEST_POSTS, SELECT_SUBREDDIT } from '.. /actions'; const selectedSubreddit = (state = 'reactjs', action) => { switch (action.type) { case SELECT_SUBREDDIT: return action.subreddit default: return state } } const posts = (state = { isFetching: false, didInvalidate: false, items: [] }, action) => { switch (action.type) { case INVALIDATE_SUBREDDIT: return { ... state, didInvalidate: true } case REQUEST_POSTS: return { ... state, isFetching: true, didInvalidate: false } case RECEIVE_POSTS: return { ... state, isFetching: false, didInvalidate: false, items: action.posts, lastUpdated: action.receivedAt } default: return state } } const postsBySubreddit = (state = { }, action) => { switch (action.type) { case INVALIDATE_SUBREDDIT: case RECEIVE_POSTS: case REQUEST_POSTS: return { ... state, [action.subreddit]: posts(state[action.subreddit], action) } default: Return state}} /** * equivalent to const rootReducer = combineReducers({postsBySubreddit: postsBySubreddit selectedSubreddit: SelectedSubreddit}) * inside is state: stateReduceHandle */ const rootReducer = combineReducers({ postsBySubreddit, selectedSubreddit }) export default rootReducerCopy the code

We can clearly see the reducer look, we directly look at the core combineReducers

3, combineReducers

// Export default Function combineReducers(reducers) {const reducerKeys = object.keys (reducers) const // Export default Function combineReducers(reducers) {const reducerKeys = object.keys (reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; I ++) {const key = reducerKeys[I] Ensure that the incoming reducer is function if (Typeof Reducers[key] === 'function') {finalReducers[key] = reducers[key]}} const FinalReducerKeys = Object.keys(finalReducers) // The combination returned is currentReducer(currentState, action) return function combination(state = {}, NextState const nextState = {} for (let I = 0; i < finalReducerKeys.length; I ++) {const key = finalReducerKeys[I] const Reducer = finalReducers[key] /** This is the state itself * state: {* reducerKey1: { * * }, * reducerKey2: { * * }, * ... PreviousStateForKey = state[key] // Call the current reducer to generate a new state const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey ! == previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length ! == object.keys (state).length // Return state hasChanged? nextState : state } }Copy the code

Therefore, what combineReducers actually does is combine multiple reducer into a total combination reducer, The combination Reducer will be called in the createStore as currentReducer to generate the latest state, and then notified to each place that uses state, ba-ba-ba-….

Third, Middleware

1, concept,

Enhancer is used to enhance the store, with middleware, time travel, persistence, etc. The only store enhancement function redux provides is applyMiddleware.

Middleware: That is, functions used to enhance store functionality, specifically, to extend Dispatch

2, Applymiddleware

On the first source

// =====index.js=========== import { createLogger } from 'redux-logger' const middleware = [ thunk ] if (process.env.NODE_ENV ! == 'production') {// Middleware is an array, middleware.push(createLogger())} Const store = createStore(reducer, applyMiddleware(... middleware) ); // =====createStore.js=========== // export default function createStore(reducer, preloadedState, enhancer) { // ... Omit, // if (typeof enhancer! == 'undefined') { // if (typeof enhancer ! == 'function') {// throw new Error('Expected the enhancer to be a function.') // return enhancer(createStore)(reducer, preloadedState) // Omit, / /} / / = = = = = applyMiddleware js = = = = = = = = = = = / / function into the refs'... Export Default Function applyMiddleware() export default Function applyMiddleware(... Middlewares) {// Return a function that receives createStore as an argument. Args is actually the second parameter of enhancer in createStore (reducer, preloadedState) return createStore => (... Args) => {// Get current store const store = createStore(... args) 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(... Args)} // Middlewares is called outside and used inside, so it's easy to write. Middlewares. map(Middleware => middlewareAPI) const chain => middlewares.map(Middleware => middlewareAPI // Send store.dispatch to multiple middleware dispatch = compose(... Chain)(store.dispatch) // Update the dispatch in store. Return {... store, dispatch } } }Copy the code

3, middleware

@template DispatchExt Extra Dispatch signature Added by this Middleware. * @template S The type of the state supported by this middleware. * @template D The type of Dispatch of the store where this Middleware is installed. */ / MiddlewareAPI<D, S> export interface Middleware< DispatchExt = {}, S = any, D extends Dispatch = Dispatch > { (api: MiddlewareAPI<D, S>): ( next: Dispatch<AnyAction> ) => (action: any) => any }Copy the code

4, compose

Source code is very refined

export default function compose(... Funcs) {if (funcs.length === 0) {return arg => arg} // If there is only one middleware, return it directly if (funcs.length === 1) {return funcs[0]} Return funcs.reduce((a, b) => (... args) => a(b(... args))) }Copy the code

5, Curry

A curried function is a function that takes multiple arguments

one at a time.

Given a function with 3 parameters, the curried version will take one argument and return a function that takes the next argument, which returns a function that takes the third argument. The last function returns the result of applying the function to all of its arguments.

A Currization function is one that takes more than one argument at a time. Given a function with three arguments, the current version takes one argument and returns a function that takes the next argument, which returns a function that takes the third argument. The last function returns the result of applying the function to all parameters.

// =======demo1 const add = a => b => a + b; const result = add(2)(3); // => 5 // =======demo2 const g = n => n + 1; const f = n => n * 2; const h = x => f(g(x)); h(20); Const compose = (g,f) => x => f(g(x)); What if there are many parameters? You can't keep writing like this... const compose1 = (g,f,h,j) => x => j(h(f(g(x)))); // compose2 is easy to understand, the first call returns a reduce function that accepts x as its initial value,x as its reduce init value const compose2 = (... fns) => x => fns.reduceRight((y, f) => f(y), x); // compose3 = reduce const compose3 = (... funcs)=> funcs.reduce((a,b) => (... args) => a(b(... Var test2 = compose2(add1,add6,mul); var test2 = compose2(add1,add6,mul); var test3= compose3(add1,add6,mul); test2(10) // => 1007 test3(10) // => 1007 // =======demo3 const map = fn => mappable => mappable.map(fn); We can call fn for each element of the array in turn using 1: Var arr = [1,2,4,5] testMap(arr) // => [7,8,10,11] const arr = [1, 2, 3, 4]; const isEven = n => n % 2 === 0; const stripe = n => isEven(n) ? 'dark' : 'light'; const stripeAll = map(stripe); const striped = stripeAll(arr); log(striped); // => ["light", "dark", "light", "dark"]Copy the code

Conclusion:

It’s the same picture