The birth of
Redux is a complete front-end state management framework, which was first proposed by Dan in 2015. It is inspired by several important features of Flux (centralized management of update logic and not allowing applications to modify data directly).
With the increasing complexity of JavaScript single-page application development, the increasing number of states and the many-to-many relationship between model and View has made state difficult to control and unpredictable. Redux is designed to address these issues.
The three major characteristics
I. Single Source of Truth
Redux takes the object tree out of the component and puts it into an external store. Globally, there is only one unique store, and the store is responsible for providing all the state of the application. This makes it easier for components to communicate with each other. There is no need to consider the hierarchical relationship between components and how to transfer data. All components only need to communicate with store, and the change of store will cause the change of related components.
In addition, benefiting from a single State tree, bug tracking is more accurate simply by checking the current state for correctness; Features like undo/redo, which were previously difficult to implement, are now easy to implement.
(Photo credit:Css-tricks.com/learning-re…
State is Read-Only.
The store itself has only four methods:
store.dispatch(action)
store.subscribe(listener)
store.getState()
replaceReducer(nextReducer)
As you can see, there is no method to change the state (setting state), and the only way to change the state is to trigger an action (a common object that describes an event that has occurred).
var action = {
type: "ADD_USER".user: { name: "Dan"}};// Suppose a store object has been created
store.dispatch(action);
Copy the code
The advantage of forcing action to describe all changes is that you have a clear idea of what’s going on, and they can be played back for log printing, serialization, storage, post-debugging, or testing.
3. Pure functions update store
Pure function: The output depends entirely on the input parameters, making it easy to test and predict the results.
To describe how an action changes the State Tree, you need to write reducers. Reducer is a function that receives state and action and returns a New state — state + Action = New State.
// Reducer Function
var someReducer = function(state, action) {...return state;
}
Copy the code
Because reducers are just functions, you can control the order in which they are called, pass in additional data, and even write reusable reducers to handle some common tasks, such as pagers.
To understand the Action
An Action is just a generic object that describes changes, and it is the only way to change store data. Transfer data from application to store via store.dispatch(Action).
We agreed that a type field of type string must be used within the action to indicate the action to be performed (identified by Type). Such as:
{
type:'ADD_TODO'.text: 'Build my first Redux app'
}
Copy the code
With the exception of the Type field, the structure of the Action object is entirely up to you.
Some advice
1. Use of boilerplate files
In most cases, type is defined as a string constant. For small applications, it’s easier to use strings for action types. However, as your application grows larger, it is recommended to have a separate module or file for your actions.
// actionTypes.js
const ADD_TODO = "ADD_TODO";
Copy the code
import { ADD_TODO } from ".. /actionTypes";
// action
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Copy the code
What’s the good of that?
- Helps maintainConsistency of namingBecause of all
action type
Summary in the same place. - Avoid double naming (someone may already have added the action you need).
- Help everyone on the teamKeep track of new featuresScope and implementation of. in
Pull Request
You can see all the additions, deletions, and modificationsAction types
List record
2. How to construct action (refer to Flux standard)
Action must be:
- A normal JavaScript object
- Using a string type
type
Field: As an identifier (ID) for the action to be performed
Actions can have:
-
Error: All values other than true (including undefined and null) should be false
-
Payload field:
-
Any action information that does not belong to the action type or state;
-
When error is true, payload acts as an error message
-
-
Meta field: Information other than payload
Action should not contain excepttype
,error
,payload
,meta
Fields other than.
Action Creator (Create function)
Action Creator (Create function) is the method that generates Action.
function addTodo(text) {
return {
type: ADD_TODO,
text,
};
}
Copy the code
Doing so will make the action creation function easier to port and test.
Redux initiates a Dispatch process by passing the results of the action creation function to the Dispatch () method.
dispatch(addTodo(text));
Copy the code
Or create a bound action creation function to automatically dispatch and call them directly:
const boundAddTodo = (text) = > dispatch(addTodo(text));
boundAddTodo(text);
Copy the code
bindActionCreators
ActionCreators (Function or Object): An Action Creator, or a value is the Object of the Action Creator.
Dispatch (Function): A dispatch Function provided by a Store instance.
bindActionCreators(actionCreators, dispatch)
BindActionCreators () is a utility function that helps you use action more easily. It can automatically bind multiple action creation functions to the Dispatch () method.
BindActionCreators can be used when you need to upload ActionCreator down to a component, but you don’t want the component to be aware of Redux, and you don’t want to send Dispatch or Redux Store to it.
addTodo = bindActionCreators(addTodo, store.dispatch);
addTodo();
// Equivalent to dispatch(addTodo(text));
Copy the code
Understand the Reducer
A Reducer is a pure function that updates state based on action. It receives the old state and action and returns the new state.
; (previousState, action) => newStateCopy the code
keepreducer
Purity is very important. Never in the worldreducer
To do these operations:
- Modify the passed parameter;
- Perform operations that have side effects, such as API requests and route jumps;
- Call an impure function, such as
Date.now()
或Math.random()
const initialState = {
visibilityFilter: "SHOW_ALL"};// action
{
type: 'SET_VISIBILITY_FILTER'.filter: 'SHOW_COMPLETED'
}
// reducer
// Specify the default value initialState for state
function todoApp(state = initialState, action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return {
...state,
visibilityFilter: action.filter,
};
default:
returnstate; }}Copy the code
Note in actual development:
- Do not modify state.use
Object.assign()
Or the object expansion operator ({... state, ... newState }
A new copy is created. - Return the old state in default.
Split Reducer
When there are multiple actions, it looks like this:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return {
...state,
visibilityFilter: action.filter,
};
case ADD_TODO:
return {
...state,
todos: [
...state.todos,
{
text: action.text,
completed: false,}]};case TOGGLE_TODO:
return {
...state,
todos: state.todos.map((todo, index) = > {
if (index === action.index) {
return {
...state,
completed: !todo.completed,
};
}
returntodo; })};default:
returnstate; }}Copy the code
This code is redundant and difficult to read, so in this case we split todos and visibilityFilter:
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false,},];case TOGGLE_TODO:
return state.map((todo, index) = > {
if (index === action.index) {
return {
...state
completed: !todo.completed,
};
}
return todo;
});
default:
returnstate; }}function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter;
default:
returnstate; }}function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action),
};
}
Copy the code
Note that each Reducer is responsible for managing only its share of the global state. State parameters of each Reducer are different, corresponding to the part of state data it manages.
combineReducers
combineReducers(reducers)
CombineReducers merges an object that has multiple reducer functions as values into a final Reducer function.
import { combineReducers } from "redux";
const todoApp = combineReducers({
visibilityFilter,
todos,
});
export default todoApp;
Copy the code
This is exactly the same as the following:
export default function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action),
};
}
Copy the code
Understand the Store
At its core, Redux has a single store.
Store can be said to be a virtual concept, which is the object that links state, Dispatcher (Action) and Reducer together. The Store has the following responsibilities:
- Maintain application state;
- provide
getState()
Method to get state; - provide
dispatch(action)
Method update state; - through
subscribe(listener)
Register listeners; - through
subscribe(listener)
The returned function unlogs the listener.
CreateStore createStore
createStore(reducer, [preloadedState], enhancer)
Parameters:
- reducer (Function)
- [preloadedState] (any): indicates the initial state.
- Enhancer (Function): Store enhancer is a higher order Function that combines Store Creator and returns a new enhanced Store creator
Initiate Actions
import { createStore } from "redux";
function todos(state = [], action) {
switch (action.type) {
case "ADD_TODO":
return state.concat([action.text]);
default:
returnstate; }}let store = createStore(todos, ["Use Redux"]);
// Logs are printed each time state is updated
// Notice that subscribe() returns a function to unsubscribe listeners
const unsubscribe = store.subscribe(() = > console.log(store.getState()));
/ / initiate the action
store.dispatch({
type: "ADD_TODO".text: "Read the docs"});// print: ['Use Redux', 'Read the docs']
// Stop listening for state updates
unsubscribe();
Copy the code
Because they are pure functions, we can define the behavior of the program before we develop the interface. At this point, you can write reducer and Action to create function tests.
Redux series
- Redux (1) : Three pieces (Action, Reducer, Store)
- Redux (2) : Develop applications with React
- Redux (3) : Middleware and Asynchronous Action
- Redux (4) : How to organize “Action” and “Reducer” files?
- Redux (5) : How can immutable data be used to improve performance?