redux
Why Redux
Is a one-way communication between components, data flow, data passed down from the top-level components through the props, the lower component is can not transfer data up, if you need to need to modify the transmission of data, then the lower component needs through the top-level components passed a method, the lower component, when the project is more and more big, Passing data between components becomes difficult, and the code looks illogical.
Redux acts as a data management module in React
What is the story
Usage scenarios
- The same state needs to be shared among multiple Components
- The state of a component that needs to be shared
- A state needs to be available anywhere
- One component needs to change the state of another component
- A component needs to change global state
The installation
npm install redux
Copy the code
npm install react-redux
Copy the code
Store using
PS: Store is independent, written in the entry file (index.js)
Create the store
- Redux offers
createStore
This function is used to generate a Store createStore
The function takes another function as an argument and returns the newly generated Store object
// In redux, the key method is removed to create a store
inport { createStore } from 'redux'
// Custom method name
function fn() {
// Specify the operation
}
const store = createStore(fu)
Copy the code
Get Store data
- To transfer data
- In the React-redux export method, it is used to wrap the component, passing the store as a property argument
// Package the component with the export method
import { Provider } from 'react-redux'
<Provider store={ store }> <App> </Provider>
Copy the code
- Receive within a component
- You’ll need to use the react-Redux tool again
- For method calls, call time can pass a parameter, the parameter can return within a data, also can get the data, finally the data will be in the form of props to the component, syntax requires the current after the function call, you also need to return a function, the function to call, receive a parameter, This parameter is used to specify which component is receiving the data
- The connect function takes an argument with the usual name
state
This parameter stores the data that is passed in
import { connect } from 'react-redux'
// Props stores the data passed from the store
function Head(props) {
return (
<div>{props.count}</div>)}const mod = (state) = > ({
// We need to store the internal values, because the values store a lot of data, only the component related can be used
count: state.count
})
// The utility function needs to be exported here
export default connect(mod)(Head)
Copy the code
Modify Store Data (Action)
The Action directive is itself an object with a Type attribute inside and its value is a string, just like defining a custom event at present. At this time, this directive is passed to store, then to reducers, and then to make some logical judgments and return the value to Store.
- The component whose data is to be modified is triggered internally by an event or other means
The dispatch method inside the data transferred from the store is action, which is an object. The object uses the Type attribute, passes a name similar to custom event, receives the type value in reduce, and processes the data
- Components that modify data
import React from 'react'
import { connect } from 'react-redux'
function Counter ( props ) {
return (
<>
<button onClick={() = > {
props.dispatch({ type: 'install' })
} }>+</button>
<p>{ props.count }</p>
<button onClick={() = > {
props.dispatch({ type: 'xxxxxxx' })
} }>-</button>
</>)}const instaiu = (state) = >({
count: state.count
})
export default connect(instaiu)(Counter)
Copy the code
- Reduce receives and processes the packets internally
/* eslint-disable import/no-anonymous-default-export */
const instalssss = {
count: 10
}
export default (state = instalssss,action ) => {
// We need to use the switch statement here because there can be many branches
switch (action.type) {
case 'install':
return {
count: state.count + 1
}
case 'xxxxxxx':
return {
count: state.count - 1
}
default:
return state
}
}
Copy the code
Extract action as a function
Pass a second parameter to connect
function Counter ( props ) {
return (
<>// No parameter is required<button onClick={props.increment}>+</button>// Parameters need to be passed<button onClick={()= > {props.increment_n(5)}}>+</button>
</>)}const mapDispatchToProps = dispatch= > ({
// method, no second argument required
increment:function () {
dispatch({
type: 'increment'})},// The second argument needs to be passed
increment_n(payload) {
dispatch({
type: 'increment_n',
payload
})
}
})
export default connect(instaiu,mapDispatchToProps)(Counter)
Copy the code
PS: These methods, which are passed to props, can be used with the props call
Automatically generate action trigger functions
Set up the automatic generation of action triggering functions by using methods inside redux
- Introduce methods
import { bindActionCreators } from "redux"
Copy the code
PS: The method is to receive two parameters internally. The first parameter is an object and the second parameter is Dispatch
bindActionCreators({ },dispatch)
Copy the code
- The action information to be configured is stored in a separate file. Each component has its own file to manage its own methods and export them to the component for use
const increment = function () {
return { type: 'increment'}}const increment_n = function () {
return { type: 'increment_n'}}export { increment,increment_n }
Copy the code
- Internal use of components
When exporting, you can export all the data and receive the data with an alias
// Alias export
import * as alink from '.. /Store/Actions/Counter.actions'
// Accept the exported method
import { increment,increment_n } from '.. /Store/Actions/Counter.actions'
Copy the code
A reference is made below
const mapDispatchToProps = dispatch= > (
bindActionCreators({increment, increment_n },dispatch)
)
export default connect(mapStateToProps,mapDispatchToProps)(Counter)
Copy the code
Set the action type constant
The reason for defining the value of type as a constant is that the value of type is used in multiple places and to avoid errors when writing later without syntax hints. Therefore, the value of type is defined as a constant
export const INCREMENT ='increment'
export const INCREMENT_N ='increment_n'
Copy the code
- Which page to use, import can be used
import { INCREMENT,INCREMENT_N } from '.. /Action_types/Counter.action.types'
const increment = function () {
return { type: INCREMENT }
}
const increment_n = function () {
return { type: INCREMENT_N }
}
export { increment,increment_n }
Copy the code
Reducer merge and split
PS: What is internal reducer doing? Obtain data from the store, change the data inside the store according to the corresponding instructions, and then the processed data will be rendered again
- Why split
Because there are many components in an application, each of which has its own data, it is impossible to store them all in the same Reducer. If data management is implemented, it is messy to manage data in the same file, so each reducer needs to create its own reducer
- Why merge
Merge with multiple reducer and merge with internal methods provided by REdux
// Inside the Reducer folder, pass an index.js file that is used to merge multiple reducer files
import { combinerReducers } from 'redux'
// Bring in each reducer
// Call methods to merge data
export default combinerReducer({
// The parameters receive an object. For easy use, each key in the reducer is the same as the reducer name of its own component for later managementKey: Imported reducer// (the key has the same name as the import)
})
Copy the code
case
import { combinerReducers } from 'redux'
import counterReducer from 'path'
import personReducer from 'path'
export default combinerReducer({
counter : counterReducer,
person: personReducer
})
Copy the code
After the merge, convert to the following format
{counter: {count}.person: [] {}}Copy the code
After the content is imported, parameters are passed through store
conclusion
Folder structure
- Action_types folder
This folder stores variables and internal variables of type configuration. The purpose is to use them in many places, and also to cause inconsistent names when type attribute is written later, so that the program cannot run normally
- The Actions folder
Is a folder for dispatch configuration options, with each component having its own configuration options
- Raducer folder
Is used to store data and change the data method, the main internal processing of data, and data storage, each component corresponds to one.
How to get data after Reducer merge
export default combinerReducer({
counter : counterReducer,
person: personReducer
})
Copy the code
After the data is merged, print the merged result and use the corresponding data based on the key name
How do components get and modify data internally
- How does a component get data internally
First, through the Connect method, he receives two callback functions. The first is to fetch the data from the Store, and the second is to have react automatically generate an Actions execution function. The execution function is generated using bindActionCreators, which is used to modify the data operation.
Parameter passing and receiving
- The first step is to configure dispatch, where you can configure the second parameter, which is the data option to pass
- Second, the component passes parameters
- The third step is to receive and process the parameters in the Reducer of data processing
Redux middleware
Why middleware
For example, in the process of project development, data is not synchronously acquired, or some asynchronous operations need to be completed before processing data. In this case, events sent through action are transmitted to middleware for processing, and then returned to store for use.
- When is it triggered
When a component sends an action directive, the middleware function fires
Registered middleware
- The introduction of the package name
import { createStore,applyMiddleware } from 'redux'
function reducer( state,action ) {
return state
}
// Define a middleware function
function middle() {
// Internally, specify that you need to return a function
return function () {
// asynchronous processing function, need to return a function
return function () {
When the action is triggered, the asynchronous action is completed}}}constStore = createStore(Reducer,applyMiddleware)Copy the code
ApplyMiddleware, which internally writes the name of a function that you want to use as middleware, writes the function name in the argument
Middleware function parameters
// 3. Receive two parameters getState and dispatch
function middle({ getState, dispatch }) {????? Unclear function20
// 2. The function is to receive the actio3n returned below
return function (next) {
// 1. The parameter receives the instruction sent by the action
return function ( action ) {
// Simulate asynchrony
setTimeout(() = > {
// If you need to manipulate some data and pass it later, you can pass it to the action via properties
action.payload = 100
// After the action is done here, you still need to hand the action to the store
return next(action)
},1000)}30.}}Copy the code
Redux-saga asynchronous reject solution
Click the button on the page to dynamically obtain the server data
-
Step 1: Install the package
-
npm install redux-saga Copy the code
-
-
Step 2: Introduce saga into the entry file and perform the related operations
-
import { createStore,applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' Copy the code
-
When using saga, instead of directly turning the function into middleware, you call the function first and return the value as middleware
-
const sagaMiddleware = createSagaMiddleware() const store = createStore(toList,applyMiddleware(sagaMiddleware)) Copy the code
-
-
Step 3: Create a separate folder for the Saga configuration, as each component will have its own Saga file
-
Note that if the saga file is configured, the requested parameters will not be passed inside the Action file, but will be passed directly inside the saga file
-
import axios from 'axios' / / (a) import { takeEvery, put } from 'redux-saga/effects' / / (3) function* loadPerson () { // Process the request and return the requested data // - Get data let persons = yield axios.get('http://localhost:3005/api/getUsers').then(res= > res.data) // Resend a dispatch yield put({type: 'load_oerson_success'.payLoad:persons}) } / / (2) export default function* personSage () { // The first argument is which method to receive, and the second argument is a function to process the request to be processed, etc yield takeEvery('load_person',loadPerson) } Copy the code
-
- First, export the methods in the method. Method 1 is used to intercept the operation, and method 2 is used to return a new instruction, which is equivalent to triggering a Dispatch operation
- Export a method with intercepting instructions inside, and a second argument to process the request and pass the result of the request
- Request the data operation, and then send the data back
-
-
-
Step 4: Merge multiple Saga files
-
import { all } from 'redux-saga/effects' import personSage from './person.saga' export default function* rootSaga () { yield all([ personSage() // Multiple files can be written with a comma])}Copy the code
-
-
Step 5: Receive the re-sent instructions inside the Reducer file and process the results
-
function reducer (state = innerText,action) { switch(action.type){ case 'load_oerson_success': return { person: action.payLoad } default: return state } } Copy the code
- The case in the reducer file is not the previous instructions, but the instructions sent by PUT in the saga file
-
-
Step 6: Finally, receive the saga file in the index file, export the merged saga file, and then use the run method. The run method receives the saga file export method.
-
// Need to write after store creation // Export the merged saga file import rootSaga from './Store/Saga/person.saga' sagaMiddleware.run(rootSaga) Copy the code
-
Simplify actions and Reducer
Installing the Tool Package
npm i redux-actions
Copy the code
-
To simplify the actions
-
import { createAction } from 'redux-actions' // expore const increment = () => ({ type: 'increment' }) // Call the method to pass the type of type that you currently want to pass expore const increment_action = createAction('increment_action') Copy the code
-
-
Simplify the reducer
-
// If the name needs to be the same as the current configured high name, you can alias the method import { handleAction } from 'redux-action' // Import directives import { increment_action } from 'Action Configuration path' // Initialize the data const initText = { const :10 } const createReducer = handleAction({ // If you use the imported instruction directly, it will be parsed as a string, so use [] directly to wrap it [increment_action]:(state,action) = > { return // Perform the operation } }, initText) // Export the result export default createReducer Copy the code
-
The first parameter is an instruction, the second parameter is the initial value of the current reducer, and other files can be used normally
-