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.