This is the second day of my participation in the August Text Challenge.More challenges in August

Redux’s core idea

Redux is a JavaScript state container that provides predictable state management.

It says:

  • A Web application is a state machine, and there is a one-to-one correspondence between views and states.
  • All the states are stored in one object.

Let’s first look at the concrete embodiment of the three concepts of “state container”, “view and state one-to-one correspondence” and “an object”.

As shown in the figure above, a Store is a state container in Redux that stores all state data, each state corresponding to a view.

Redux also states that one State corresponds to one View. As long as the State is the same, the View is the same, and if you know the State, you know what the View looks like, and vice versa.

For example, if the current page has three states: Loading, SUCCESS, or error, each of these states corresponds to a unique view.

Now that we know about “state containers” and “view to state mapping,” how does Redux achieve predictability? Let’s take a look at the Redux workflow.

First, let’s look at a few core concepts:

  • Store: A place where data is stored. You can think of it as a container. There can only be one Store for the entire application.
  • State: The Store object contains all the data. If you want to obtain the data at a certain point in time, you need to generate a snapshot of the Store. The data set at this point in time is called State.
  • Action: State changes, resulting in View changes. However, the user does not touch State, only View. Therefore, the change in State must be caused by the View. An Action is a notification from the View that the State should change.
  • Action Creator: The View will have as many actions as it wants to send. It would be cumbersome to write them all by hand, so let’s define a function to generate Action, called Action Creator.
  • Reducer: After the Store receives the Action, it must present a new State so that the View will change. This State calculation process is called Reducer. Reducer is a function that takes Action and the current State as parameters and returns a new State.
  • Dispatch: is the only way for a View to issue an Action.
  • Subscribe: Changes to the subscription data. Whenever the state changes, the callback is executed.

Then let’s go through the workflow:

  1. First, the user issues an Action (via the View) using the Dispatch method.
  2. The Store then automatically calls the Reducer with two parameters: the current State and the received Action, and the Reducer returns the new State
  3. Whenever the State changes, the Store calls a listener function to update the View.

At this point, a user interaction flow ends. As you can see, the data flows in one direction throughout the process, which ensures clarity.

Source code analysis

Source code analysis using simplified version of the code, click to see the full code

createStore

CreateStore is the main process for Redux

export default function createStore(reducer, preloadedState, enhancer) {
  / / middleware
  if (enhancer) {
    return enhancer(createStore)(reducer, preloadedState)
  }
  let currentReducer = reducer
  let state = preloadedState
  let listeners = []

  function getState() {
    return state
  }

  function subscribe(listener) {
    let isSubscribed = true
    listeners.push(listener)
    return function unsubscribe() {
      if(! isSubscribed)return
      isSubscribed = false
      listeners.splice(index, 1)}}function dispatch(action) {
    state = currentReducer(state, action)
    listeners.forEach(listener= > listener())
    return action
  }

  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: Symbol('REPLACE')})return store
  }
  // Initialize state when preloadedState is not passed
  dispatch({ type: Symbol('INIT')})return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
  }
}
Copy the code

The core code is this

let currentReducer = reducer
let state = preloadedState
let listeners = []

function subscribe(listener) {
  let isSubscribed = true
  listeners.push(listener)
  return function unsubscribe() {
    // Prevent repeated calls
    if(! isSubscribed)return
    isSubscribed = false
    listeners.splice(index, 1)}}function dispatch(action) {
  state = currentReducer(state, action)
  listeners.forEach(listener= > listener())
  return action
}
Copy the code

As you can see, the Subscribe function adds the listeners we pass in to the listeners array, and then executes each listener at dispatch to update the listeners

The SUBSCRIBE function also returns an unsubscribe function that unsubscribes the listener

combineReducers

CombineReducers is used to merge multiple reducer functions

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  return function combination(state, action) {
    const nextState = {}
    reducerKeys.forEach(key= > {
      const reducer = reducers[key]
      // The state of the previous key
      const previousStateForKey = state[key]
      // Run reducer to obtain the new state
      const nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
    })
    return nextState
  }
}
Copy the code

In fact, the reducer and state are obtained according to different keys and then updated

applyMiddleware

ApplyMiddleware is a function that implements middleware support

Compose (f, g, h) : compose(f, g, h) : args) => f(g(h(... args)))
const compose = (. funcs) = >
  funcs.reduce(
    (a, b) = >
      (. args) = >a(b(... args)) )export default function applyMiddleware(. middlewares) {
  return createStore= > (reducer, preloadedState) = > {
    const store = createStore(reducer, preloadedState)
    const middlewareAPI = {
      getState: store.getState,
    }
    const chain = middlewares.map(middleware= > middleware(middlewareAPI))
    constdispatch = compose(... chain)(store.dispatch) store.dispatch = dispatchreturn store
  }
}
Copy the code

The key to this step is that const dispatch = compose(… Chain (store.dispatch) combines middleware

So far we have implemented the main features of Redux, but I won’t go into details. Now let’s take a look at the workflow of Redux.

Refer to the article

Redux from design to source

Totally understand Redux