The entire global state of an application is stored in a single Store object tree (single shared datastore). The only way to change the state is to create an action (an object describing what happened), dispatch it to a store, and write a pure Reducer function that evaluates the new state based on the old state and actions.

state

With a single global data source, where all the state is stored in a single tree, hard-to-implement state management suddenly becomes trivial and the state is externally read-only

action

The only way to change state is to issue an action (an object that describes what happened); All changes are centrally managed and occur in a strict sequence

reducer

A pure function that takes the previous state and an action and returns the next state; The new state object is returned instead of changing the previous state

store

An object that holds the application state tree. There should be only one store in the Redux application because the merge occurs at the Reducer level.

Write a story

Implement a unique data source using a singleton pattern

const createStore = (() = > {
  let store = null
  const state = {}
  const getState = () = >{}const dispatch = () = >{}const subscribe = () = >{}const unSubscribe = () = > {}
  const replaceReducer = () = >{}return () = > {
    if(! store) { store = { getState, dispatch, subscribe, replaceReducer, } }return store
  }
})()

export default createStore
Copy the code

CreateStore () is used to retrieve a store, and multiple calls generate the same store, which contains

  • dispatch(action)Basic scheduling functions.
  • getState()Returns the current state of the store.
  • subscribe(listener)Registers a function to be called when the state changes.
  • replaceReducer(nextReducer)Replace the Reducer of the store to calculate state, dynamically pretend some reducer, and realize hot overload

GetState returns a copy of state to prevent consumers from directly modifying their referenced properties, using LoDash’s cloneDeep method

const getState = () = > {
    return _.cloneDeep(state)
}
Copy the code

Add and remove subscribers

const listeners = [];
const subscribe = (fn) = > {
    listeners.push(fn)
}
const unsubscribe = (fn) = > {
    const index = listeners.indexOf(fn)
    listeners.splice(index, 1)}Copy the code

Add and remove are usually put together using closures, with the remove function as the return value of the add

const listeners = [];
const subscribe = (fn) = > {
  listeners.push(fn)
  const unsubscribe = () = > {
    const index = listeners.indexOf(fn)
    listeners.splice(index, 1)}return unsubscribe
}
Copy the code

Initialize the Reducer and add the function to modify the Reducer

let currentReducer = (state, action) = > {
  return state;
}
const replaceReducer = (reducer) = > {
  currentReducer = reducer
}
Copy the code

Finally, the trigger of subscriber message is completed by implementing Dispatch to prevent modification to source data, and the copy of data is delivered to consumers

const dispatch = (action) = > {
  const preState = _.cloneDeep(state)
  state = currentReducer(_.cloneDeep(state), action)
  for (let i = 0; i < listeners.length; i++) {
    const listener = listeners[i]
    listener(preState, _.cloneDeep(state))
  }
}
Copy the code

The complete code is as follows:

import _ from 'lodash'
const createStore = (() = > {
  let store = null
  let state = {}
  const listeners = []
  let currentReducer = (state, action) = > {
    return state;
  }
  const getState = () = > {
    return _.cloneDeep(state)
  }
  const dispatch = (action) = > {
    const preState = _.cloneDeep(state)
    state = currentReducer(_.cloneDeep(state), action)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener(preState, _.cloneDeep(state))
    }
  }
  const subscribe = (fn) = > {
    listeners.push(fn)
    const unsubscribe = () = > {
      const index = listeners.indexOf(fn)
      listeners.splice(index, 1)}return unsubscribe
  }
  const replaceReducer = (reducer) = > {
    currentReducer = reducer
  }
  return () = > {
    if(! store) { store = { getState, dispatch, subscribe, replaceReducer, } }return store
  }
})()

export default createStore

Copy the code

Example:

const store = createStore()
store.subscribe((prev, v) = > console.log(prev, v, 'First consumer'))
store.subscribe((prev, v) = > console.log(prev, v, 'Second consumer'))
store.replaceReducer((state, action) = > {
  const{ type, ... rest } = actionif (action.type === 'test') {
    return{... state, ... rest, } }else {
    return state
  }
})
store.dispatch({ type: 'test'.value1: 'testValue1' })
store.dispatch({ type: 'test'.value2: 'testValue2' })
store.dispatch({ type: 'other'.value: 'other' })
Copy the code

There are two subscribers who have implemented reducer after 3 dispatch and obtained the new state result