Preface,

I have learned RN development for some time. During this time, MY biggest feeling is that the most troublesome aspect of RN page development is state management. RN builds the page based on the front-end framework React. React provides the setState method for the class component to update the component state at the beginning. This method has its own limitations. React then introduced a functional component that could use Hooks, which essentially split State in a granular manner, allowing functional components to save State. However, the problem of State management was still not solved. To solve this problem, some State management libraries emerged, among which Redux was the most famous. Although Redux claims to have been built specifically for React and can be used with a variety of JS projects, it’s likely that few people use Redux in frameworks other than React, including the Redux documentation examples that are based on React ~

See this article for an introduction to using Redux, which is more of a theoretical discussion.

1. In-depth understanding of Store, Action and Reducer

1.1 store

Start with a question: What is a store, and why is it needed?

1.1.1 What is a store

Store means’ store ‘in English, and as the name suggests, is a place where state is stored.

1.1.1 Why store

We already know that a store is a place to store state, so what is state? State is the data that fills the UI. The state can be a line of text, it can be a Boolean, it can be a number… It can also be arrays or objects made up of these basic types, and we use these state data to populate the current UI style.

Why do we need a Store? Naturally, we need a place to store the state of the current page, and the place to store the state of the page is store. Of course, store does not have to store all the states of all the components of the current page. Some states that do not need to be known by other components can be stored inside the component. Use setState or useState for state management yourself.

1.1.2 What does createStore do

In our normal use, the createStore method passes a reducer and returns a store. To get a better understanding of the createStore method, we can look at the source.

Next to the source code for some deletion, mainly to delete some boundary conditions and error, but still can ensure the use of basic functions.

import ActionTypes from './utils/actionTypes'

Reducer (mandatory), initial default state (optional), store enhance (optional)
export default function createStore(reducer, preloadedState, enhancer) {
	
  let currentReducer = reducer // Reducer assigned value currentReducer
  let currentState = preloadedState // currentState is where the state is actually saved
  let currentListeners = [] // Callbacks registered with store.subscribe are stored in this list
  let nextListeners = currentListeners // Listener copy to prevent collisions when the register or unregister methods are called during the Dispatch event

  /** * Ensure that the nextListeners are copies of currentListeners */
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  // The common method, getState, essentially returns the current state
  function getState() {
    return currentState
  }
	
  // Subscribe to subscribe
  function subscribe(listener) {
    let isSubscribed = true // The registered flag bit

    ensureCanMutateNextListeners() // make sure the nextListeners hold copies of the currentListeners
    nextListeners.push(listener) // Add listeners to the nextListeners

    // Returns the unsubscribe method, which is used to unsubscribe
    return function unsubscribe() { 
      if(! isSubscribed) {// Return if unregistered
        return
      }
      isSubscribed = false // Set the flag bit
      ensureCanMutateNextListeners() // make sure the nextListeners hold copies of the currentListeners
      const index = nextListeners.indexOf(listener) 
      nextListeners.splice(index, 1) // Remove the listener registry from nextListener
      currentListeners = null CurrentListeners will be reassigned to nextListeners at dispatch}}// Dispatch is used to send an action
  function dispatch(action) {
    currentState = currentReducer(currentState, action) // Call reducer, pass in the current state and the action to send, get the new state

    const listeners = (currentListeners = nextListeners) // Point currentListeners to nextListeners and assign values to the listeners variable
    // Call all registered listeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
		// Returns the sent action
    return action
  }

  // Replace reducer
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }
  // Send an INIT event that assigns currentState to use the default state set by the reducer
  dispatch({ type: ActionTypes.INIT })

  // Finally return a state object containing these four methods
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer
  }
}
Copy the code

The above code can be used directly in a project to replace the createStore method in the Redux library. There is no problem. The comments are written in detail.

  • Why are there currentListeners and nextListeners? A: In order to prevent conflicts caused by registering a new listener or unregistering a listener at Dispatch, for example, we are looping through the registry for a callback and suddenly a new callback is registered, or unregistering a callback and the list changes, which is not quite right. We want to call the Dispatch function, The lists traversed are immutable, so any additions and deletions to the registries are replicas of nextListeners, and do not affect the list being traversed.
  • Why dispatch({type: actiontypes.init}) is finally sent? Answer: to initialize state. In the dispatch method, reducer is called. If actiontypes. INIT is not processed by the customized reducer, the default state set by reducer is returned. If the reducer is processed, the customized state is returned.

1.2 the action

What is 1.2.1 Action

The purpose of an action is to change the current state.

1.2.2 the action structure

The structure of an action is very simple and can look like this

{
	type: 'refresh'
}
Copy the code

Type is used to identify the type of action, such as the action above, which might represent a refresh event.

Note that action is a generic object. The Redux specification requires a type field to represent the action type, so instead of using a type field, you can use a leixing field to represent the action type.

After all, reducer is also written by you, so it is not a problem to match the leixing field in reducer and return the new state, but as seen in createStore source code {type: Actiontypes.init} events are not received, so this is not recommended.

The Redux action specification has several optional fields in addition to the required field type to indicate the type of action

  • Payload: Carries data. It can be a basic data type or an object
  • Error: indicates the error information field
  • Meta: Additional description field for action

Refer to this for a more detailed specification

1.3 reducer

Reducer, reducer?

Reducer Why is reducer called? This is an interesting question, after all, store and action names are well understood, so this reducer is… Reducer?

1.3.1 Origin of reducer name

Reducer the reducer name comes from the reduce method of the JS Array object

var numbers = [65.44.12.4];
 
function getSum(total, num) {
    return total + num;
}

numbers.reduce(getSum);
// Output: 125
Copy the code

In the above code block, we defined an array of numbers and called the reduce method of the array. The method passed getSum method as parameter, and reduce method used getSum method to iterate the elements in the number group successively. This getSum function is called reducer function.

The reducer function getSum receives two parameters, total is the sum of the array elements traversed previously, num is the array element traversed currently, we add the two and return, this return value will become the total parameter of the getSum method when traversing the next element. That’s how we calculate the sum of the array elements.

Because reducer and array reduce methods in Redux use the same methods, reducer of Redux is called reducer.

I feel that reducer is not a good name. Some people who don’t know much about JS don’t know what reducer is for when they see it. I think it is more appropriate to call it StateCalculator

1.3.2 Functions of reducer

The reducer’s role is to calculate and return a new state based on the current state and the actions to be sent.

1.3.3 Reducer structure

const initialState = {
	refresh: false
}

export default function appReducer(state = initialState, action) {
  // Matches what acton is going to do based on the action's type field
  switch (action.type) {
    // Define here what the different action types should do and return the new state
    case 'refresh': {
    	return {
      	...state,
        refresh: true}}default:
      // If no type is matched, return the original state
      return state
  }
}
Copy the code

Reducer receives the current state and the sent action as parameters and returns a new modified state.

In the createStore method, we know that the Reducer method is called in the Store dispatch method and modifies the store state with its return value. Then all listeners registered with the Store. Subscribe method are iterated. The latest status update UI is used in the listener method.

conclusion

Now we know

  • The store acts as a store for state, and the current state can be obtained through the getState method. Send the action through the Dispatch method, update the state of the store, and iterate through the callback to all listeners; The subscribe method registers the callback, which is called every time the Store dispatches an action.
  • Action is a normal object that passes the action type and data
  • Reducer is a normal function that receives the current state and sent actions as parameters and returns a value of type state. Reducer acts as a state calculator to calculate a new state based on the current state and the action type of the Store dispatch.

In a word:

The essence of a redux is to listen for changes in the store, and to subscribe listeners when changes occur in the store. In particular, data changes are calculated by action and Reducer.