Preface 🤔
React
It’s a one-way data flow. Data flows throughprops
From parent node to child node. If some of the top levelprops
Changed,React
All child nodes are re-rendered. Note ⚠ ️ :props
Is read-only (that is, cannot be usedthis.props
Directly modifyingprops
), which is used to pass data and configuration throughout the component tree.- Each component has its own
state
.state
和props
The difference betweenstate
Exists only within components. Note ⚠️ : can only be called from the current componentthis.setState
Methods to modifystate
Value (Cannot be modified directlythis.state
). - As you can see, there are two ways to update a child component. One is to change the child component’s own
state
Value, and the update child received from the parentthis.props
Value to be updated. - in
React
Most of the time during project development, we need to have components share some data. In general, we can pass data between components (throughprops
However, when the data needs to be passed between non-parent-child components, it becomes cumbersome to operate and makes the code less readable, so we need to use itstate
(Status) management tool. - Common status management tools are
redux
.mobx
. Due to theredux
Provides an entire architecture for state management, with clear constraints, suitable for use in massively multiplayer applications. This article introduces how to useReact
Used in the projectredux
Perform status management.
Get down to business 🥰
- This section mainly introduces
redux
和react-router
Basic knowledge 📖 and related configurations 👩🏾💻.
redux
The basic concept
redux
This mode applies to scenarios with multiple interactions and multiple data sources. From a component perspective, we can consider using it in a project if our application has the following scenariosredux
:
- The state of a component that needs to be shared
- A state needs to be available anywhere
- A component needs to change global state
- One component needs to change the state of another component
- When our application conforms to the scenarios mentioned above, if not used
redux
Or other state management tools, if state reading and writing are not handled according to certain rules, the readability of project code will be greatly reduced, which is not conducive to the improvement of team development efficiency.
- As the picture above shows,
redux
By putting all thestate
Centralize to the top of the component, providing flexibility to allstate
Distribute to all components as needed. redux
The three principles of:- Application-wide
state
All stored in one treeobject tree
, andobject tree
Exists only in onestore
This does not mean useredux
I need to put all of thestate
enduresredux
Components can still maintain themselvesstate
). state
It’s read-only.state
Changes to the view (view
). Out of reach of usersstate
, can only touch the view, the only changestate
Is triggered in the viewaction
.action
Is a generic object that describes events that have occurred.- use
reducers
To perform thestate
The update.reducers
Phi is a pure function that acceptsaction
And the currentstate
As an argument, a new one is returned by calculationstate
To update the view.
- Application-wide
- As the picture above shows,
redux
The workflow of the- First, the user passes through the view
store.dispatch
Methods aaction
. - And then,
store
Automatically callreducers
And pass in two arguments: currentstate
And receivedaction
.reducers
Will return a newstate
。 - Finally, when
store
Listen to thestate
The listener function is called to trigger the view’s re-rendering.
- First, the user passes through the view
- ⚡⚡️⚡ port:
API
store
store
That’s where the data is stored, and there can only be one in the entire applicationstore
.redux
providecreateStore
This function is used to create astore
To store the entire applicationstate
:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);
Copy the code
- As you can see,
createStore
acceptreducer
, the initialstate
(Optional) and enhancer as arguments, return a new onestore
Object.
state
store
Object contains all data. If you want the data at a certain point in time, you have to be rightstore
Generate a snapshot. The collection of data at this point in time is calledstate
.- If you want to get the current moment
state
, can be accessed throughstore.getState()
Methods to get:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);
const state = store.getState();
Copy the code
action
state
Change, which causes the view to change. However, the user cannot reach itstate
, can only touch the view. So,state
The change must be initiated by the view.action
That’s notifications from the view, notificationsstore
At this timestate
That should change.action
Is an object. One of thetype
Attributes are required to representaction
The name of the. Other attributes can be set freely, the community has a specification to refer to:
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux'// Optional attribute};Copy the code
- The code above defines a name called
ADD_TODO
的action
, the data information it carries isLearn Redux
.
Action Creator
view
There will be as many messages as there are to sendaction
If they were written by hand, it would be very troublesome.- You can define a function to generate
action
This function is calledAction Creator
, as in the following codeaddTodo
Function:
const ADD_TODO = 'add TODO;
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
const action = addTodo('Learn Redux');
Copy the code
redux-actions
Is a practical library that lets you writeredux
State management becomes easy. The library providescreateAction
The action creator () method is used to create an action creator:
import { createAction } from "redux-actions"
export const INCREMENT = 'INCREMENT'
export const increment = createAction(INCREMENT)
Copy the code
- The code above defines an action
INCREMENT
And then throughcreateAction
Created a correspondingAction Creator
:- call
increment()
Is returned{ type: 'INCREMENT' }
- call
increment(10)
return{ type: 'INCREMENT', payload: 10 }
- call
store.dispatch()
store.dispatch()
It’s view emittedaction
The only method that accepts aaction
Object as argument:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});
Copy the code
- In combination with
Action Creator
, this code can be rewritten as follows:
import { createStore } from 'redux';
import { createAction } from "redux-actions"
const store = createStore(reducer, [preloadedState], enhancer);
const ADD_TODO = 'ADD_TODO';
const add_todo = createAction('ADD_TODO'); // Create Action Creator store.dispatch(add_todo('Learn Redux'));
Copy the code
reducer
store
receivedaction
Later, a new one must be givenstate
, so that the view is updated.state
The calculation (update) process is passedreducer
The implementation.reducer
Phi is a function that acceptsaction
And the currentstate
As an argument, return a new onestate
:
const reducer = function (state, action) {
// ...
return new_state;
};
Copy the code
- In order to implement the call
store.dispatch
Method is executed automaticallyreducer
Function needed before creatingstore
When will bereducer
The incomingcreateStore
Methods:
import { createStore } from 'redux';
const reducer = function (state, action) {
// ...
return new_state;
};
const store = createStore(reducer);
Copy the code
- In the code above,
createStore
Method acceptsreducer
As an argument, a new one is generatedstore
. Later whenever the view is usedstore.dispatch
Sent to thestore
A newaction
Is automatically calledreducer
Function to get the updatedstate
. redux-actions
provideshandleActions
Method is used to process more than oneaction
:
// handleActions(reducerMap, defaultState) import {handleActions} from'redux-actions';
const initialState = {
counter: 0
};
const reducer = handleActions(
{
INCREMENT: (state, action) => ({
counter: state.counter + action.payload
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload
})
},
initialState,
);
Copy the code
Split and merge reducer
- As mentioned earlier, in a
react
You can only have one applicationstore
Used to store applicationsstate
. Component callsaction
Function to pass data toreducer
.reducer
Update the corresponding according to the datastate
. - For large applications, yes
state
Must be so large that it causesreducer
The complexity of the
Break up
- At this time, you can consider the
reducer
Break it up into separate functions and make each function responsible for its own managementstate
Part of.
merge
redux
providescombineReducers
Auxiliary functions can be independently dispersedreducer
Merged into one finalreducer
Function, and then in the creationstore
ascreateStore
The parameter is passed in.- We can put all the pieces according to the business needs
reducer
Put it in different directories, then introduce it in one file, and finally merge itreducer
Export:
// src/model/reducers.ts
import { combineReducers } from 'redux';
import UI from './UI/reducers';
import user from './user/reducers';
import content from './content/reducers';
const rootReducer = combineReducers({
UI,
user,
content,
});
export default rootReducer;
Copy the code
Middleware and asynchronous operations
- right
redux
In terms of synchronization, it means when the view is emittedaction
Later,reducer
Work out immediatelystate
(the originalredux
Workflow), while asynchrony refers to inaction
Once issued, it is executed after a certain period of timereducer
. - Synchronization usually occurs native
redux
In most real world scenarios, asynchronous operations are needed more often:action
After issuing, before enteringreducer
An asynchronous task, such as sending, needs to be completed firstajax
Request and get the data before you enterreducer
Performs calculations and pairsstate
Update. - Apparently native
redux
Asynchronous operations are not supported, and a new tool, middleware, is needed to handle this business scenario. Essentially, middleware is rightstore.dispatch
The method is extended. - Middleware provides a location
action
After launch, arrivereducer
Previous extension point: namely passstore.dispatch
Method issuedaction
It passes through the middleware and finally arrivesreducer
.
- We can use middleware for logging (
redux-logger
), create crash reports (write your owncrashReporter
), call the asynchronous interface (redux-saga
) or routing (connected-react-router
) and other operations. redux
Provides a nativeapplyMiddleware
Method, which assembles all the middleware into an array and executes it in turn. If you want to useredux-logger
To implement the logging function, the usage is as follows:
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
const store = createStore(
reducer,
applyMiddleware(logger)
);
Copy the code
- If there are more than one middleware, the middleware is passed in as parameters in turn
applyMiddleware
Method:
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import createSagaMiddleware from 'redux-saga'; const logger = createLogger(); Const sagaMiddleware = createSagaMiddleware(); // Call the asynchronous interfaceletmiddleware = [sagaMiddleware]; middleware.push(logger); Const store = createStore(reducer, // initial_state applyMiddleware(... middleware) );Copy the code
- Note that ⚠️ is:
createStore
The method can optionally accept the initial state of the entire application. If the initial state is passed in,applyMiddleware
Need to be the third parameter.- Some middleware have order requirements that should be documented before use (e.g
redux-logger
Be sure to put it last, otherwise the output will be incorrect.
react-redux
The concept is introduced
- Described in the previous section
redux
Itself is one that can be combinedreact
.vue
.angular
Even nativejavaScript
The state library used by the application. - In order to make
redux
Help us managereact
The state of the application needs to be changedredux
与react
The connection, officially provided react-reduxLibrary (This library can be selected or used onlyredux
). react-redux
Divide all components into UI components and container components:- The UI component is only responsible for rendering the UI, and does not contain state (
this.state
), all data are provided bythis.props
Provide and do not use anyredux
The API. - Container components manage data and business logic and contain state (
this.state
), availableredux
The API.
- The UI component is only responsible for rendering the UI, and does not contain state (
- In short, the container component, as the parent of the UI component, is responsible for communicating with the outside world and passing data through
props
Render the view to the UI component. react-redux
Specifies that all UI components are provided by the user, while container components are provided byreact-redux
Automatic generation. In other words, the user is responsible for the visual layer, and the state management is all overreact-redux
.
API
The connect method
react-redux
providesconnect
Method to generate UI components into container components:
import { connect } from 'react-redux'class Dashboard extends React.Component { ... } const mapStateToProps = (state) => {returnLoading: state.loading,}} // Export container components automatically generated by the connect methodexportDefault connect(mapStateToProps, // Optional // mapDispatchToProps, // optional)(Dashboard)Copy the code
- As you can see from the code above,
connect
Method takes two optional parameters to define the business logic of the container component:mapStateToProps
Responsible for input logic, soonstate
Map to parameters passed into the UI component (props
)mapDispatchToProps
Responsible for output logic that maps user actions to UI componentsaction
- Note ⚠️ : when
connect
When the method does not pass in any arguments, the generated container component can be thought of as a wrapper around the UI component without any business logic:- omit
mapStateToProps
Parameter, the UI component will not subscribestore
, i.e.,store
Updates to the UI component do not cause updates to the UI component. - omit
mapDispatchToProps
Parameter, the UI component does not treat the user’s actions asaction
Send data tostore
, needs to be called manually in the componentstore.dispatch
Methods.
- omit
mapStateToProps
mapStateToProps
Is a function that sets up a delta fromstate
Object (external) to the UI componentprops
Object mapping. This function subscribes to the entire applicationstore
And every timestate
When updates are made, the UI component’s parameters are recalculated automatically, triggering a re-rendering of the UI component.mapStateToProps
The first argument to is alwaysstate
Object, and optionally a second argument, representing the container componentprops
Object:
// The container component code // <Dashboard showType="SHOW_ALL">
// All
// </Dashboard>
const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.showType === "SHOW_ALL",
loading: state.loading,
}
}
Copy the code
- use
ownProps
As a parameter, if the parameters of the container component change, it also causes the UI component to re-render.
mapDispatchToProps
mapDispatchToProps
是connect
The second argument to the function, which creates the UI component’s argument tostore.dispatch
Method mapping.- Because it is mostly used in projects
mapDispatchToProps
I won’t go into detail here. aboutmapStateToProps
,mapDispatchToProps
和connect
A more detailed usage description of theThe document.
The Provider component
- use
connect
After the method generates the container component, it needs to be picked up by the container componentstate
Object to generate parameters for the UI component. react-redux
providesProvider
Component, which is available to the container componentstate
, the specific usage is neededProvider
Components wrap the root component of a project (such as App) so that all of the root’s children are available by defaultstate
Object:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { store } from './store/configureStore';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));Copy the code
react-router
react-router
Is a fullreact
The routing solution that it holdsUI
与URL
Synchronization. In the project we are using the latestv4
Version.- Note that ⚠️ should not be installed directly during development
react-router
, because 👉 : inv4
Version of thereact-router
Divided into three packages:react-router
,react-router-dom
和react-router-native
, their differences are as follows:react-router
: Provides core routing components and functions.react-router-dom
: Provides routing components and functions used by the browser.react-router-native
: providereact-native
Routing components and functions used by the platform.
- When our
react
Applications are used at the same timereact-router
和redux
, the two can be further integrated to achieve:- will
router
The data andstore
And can be synchronized fromstore
accessrouter
Data, availablethis.props.dispatch
Methods to sendaction
. - through
dispatch actions
Navigation, personal understanding is availablestore.dispatch(push('routerName'))
Switch routes. - in
redux devtools
Supports time travel debugging of route changes.
- will
- To achieve the above goals, you can pass
connected-react-router
和history
Two libraries are implemented as follows:- When creating a
store
Files to add configuration, including creationhistory
Object, usageconnected-react-router
To provide theconnectRouter
Methods andhistory
Object creationroot reducer
, the use ofconnected-react-router
To provide therouterMiddleware
Middleware andhistory
Object implementationdispatch actions
Navigation.
import { connectRouter, routerMiddleware } from 'connected-react-router'; import createHistory from 'history/createBrowserHistory'; import { createStore, applyMiddleware } from 'redux'; import { createLogger } from 'redux-logger'; import createSagaMiddleware from 'redux-saga'; import reducer from '.. /model/reducers'; export const history= createHistory(); const sagaMiddleware = createSagaMiddleware(); // Call the asynchronous interfacelet middleware = [sagaMiddleware, routerMiddleware(history)]; const logger = createLogger(); // Log middleware. Push (logger); const initialState = {}; const store = createStore( connectRouter(history)(reducer), initialState, applyMiddleware(... middleware) );Copy the code
- File in the project entry
index.js
Add configuration to the root component, including usingconnected-react-router
To provide theConnectedRouter
Component package routing willConnectedRouter
Component asProvider
And will be in thestore
Created in thehistory
Object to be introduced asprops
Properties of the incomingConnectedRouter
Components:
import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Provider } from 'react-redux' import { ConnectedRouter } from 'connected-react-router' import App from './App' import rootSaga from './model/sagas'; import { store, history } from './store/configureStore'; ReactDOM.render( <Provider store={store}> <ConnectedRouter history= {history}> <App /> </ConnectedRouter> </Provider>, document.getElementById('root'));Copy the code
- When creating a
- And that’s done
react-router
和redux
Deep integration ✌️.
Conclusion 👀
- This article introduces how to use
React
Used in the projectredux
State management, and the relevant basic knowledge of the introduction and display of the complete code. - For details, see 👉 : React + typescript project customization process.
If there are any omissions in the above content, please leave a message ✍️ to point out, and progress together 💪💪💪
If you find this article helpful, 🏀🏀 leave your precious 👍
Resources 📖
- Redux or Mobx, let me solve your confusion!
- Build React applications from scratch — React Application Architecture
- Introduction to Redux — Middleware and Asynchronous Operations
- Redux Tutorial — How to use React-redux