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

  1. Redux offerscreateStoreThis function is used to generate a Store
  2. createStoreThe 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
  1. 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
  1. You’ll need to use the react-Redux tool again
  2. 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
  3. The connect function takes an argument with the usual namestateThis 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
        1. 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
        2. Export a method with intercepting instructions inside, and a second argument to process the request and pass the result of the request
        3. 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