1. The story definition

Redux is a state container for JavaScript applications that provides predictable state management.

It is recommended to start using Redux when you encounter the following problems:

  • You have a lot of data that changes over time
  • You want the state to have a single, identifiable source
  • You find that managing all state in a top-level component is not maintainable

2. Why does React use Redux

React is an abstraction layer (UI library) of the DOM, not a complete solution for Web applications. React is complicated in terms of data processing and communication between components.

For large, complex applications, these two aspects are critical. Therefore, it is difficult to write large applications using React only.

  • In 2014, Facebook introduced the concept of Flux architecture, which led to many implementations.
  • In 2015, Redux emerged, combining Flux with functional programming (Reducer), and in a short time became the most popular front-end architecture.
  • Flux is the earliest state management tool, which provides the idea of state management as well as the corresponding implementation
  • In addition to Flux and Redux, there are: Mobx and other state management tools

  • The main difference: communication between components

  • Not using Redux (figure left) :

    • React has built-in mechanisms, such as parent-child component communication and status promotion
    • Weak communication for components dealing with distant relatives (not father and son)
    • Data flows between components are chaotic and bugs are difficult to locate
  • Using Redux (figure right) :

    • Centrally stores and manages application state
    • When dealing with component communication problems, the hierarchical relationships between components are ignored
    • Simplify communication between components in large complex applications
    • Clear data flow, easy to locate bugs

3. Three core concepts of Redux

1.action

  • The action.

  • A js object containing two properties:

    • Type: Identifies the attribute. The value is a string. Multiple types are separated by action
    • Payload: Optional data attribute. Indicates the data carried by the action
  • Actions only describe the fact that something happened, not how the application updates state.

  • Features:

    • Only describe what to do
    • JS object, which must containtypeProperty to distinguish the type of action
    • According to different functions, additional data can be carried to complete corresponding functions with the data
{ type: 'add1' } 
{ type: 'addN'.payload:10 }
Copy the code

2.reducer

  • A pure function

  • role

      1. Initialization state
      2. Modify the state of
  • Modify the state of

    Returns the new state based on the old state and action passed in

    Formula :(previousState, action) => newState

const reducer = (state = 0, action) = > {
  switch (action.type) {
  
  // Execute the action add to increment state by 1
    case 'add':
      // return the new state
      return state + 1
    case 'addN':
      // return the new state
      return state + action.payload
    default:
      return state
  }
}
Copy the code

3.store

  • Store: Warehouse, core of Redux, integrate actions and reducer

  • Features:

    • An app has only one store
    • Maintain the state of the application and obtain the state:store.getState()
    • When creating a storeReceive the Reducer as the parameter:const store = createStore(reducer)
    • To initiate a status update, you need to distribute the action:store.dispatch(action)

    Const unSubscribe = store.subscribe(() => {})

    — unSubscribe status change: unSubscribe()

import { createStore } from 'redux'
import reducer from './reducer'
export default createStore(reducer)
Copy the code

Reducer split and merge

As project functionality becomes more complex, the number of states that need to be managed by Redux increases. The Reducer function becomes heavier and heavier. In this case, the reducer needs to be disassembled according to business modules and the large reducer needs to be disassembled into small files. Example:

├ ─ ─ the SRC ├ ─ ─ store # redux directory, usually call the store | ├ ─ ─ index. The js # define and store is derived. Which will import the reducer | └ ─ ─ reducers # modules of reducer | ├ ─ ─ reducer1. Js # module1The reducer | ├ ─ ─ reducer2. Js # module2The reducer | └ ─ ─ index. Js # reducer overall entrance, will import the reducer1, reducer2 ├ ─ ─ index. The js # entry documents of the project, will import and rendering App. Js ├ ─ ─ App. Js # root component, introducing the module1And the module2Component Exercises ─ modules1.Js # module1├ ─ module2.Js # module2componentCopy the code

In the SRC/store/reducers/index. Js import combineReducers reducer for consolidation

import book from './book'
import user from './user'

import { combineReducers } from 'redux'

const rootReducer = combineReducers({
  book,
  user
})

export default rootReducer
Copy the code

5.React-redux

The react – redux library

React-redux is an official redux binding library

  1. React and Redux are two separate libraries with separate responsibilities.
  2. Redux can be used with other JS libraries and frameworks, not specifically react.
  3. To implement state management using Redux in React, you need a mechanism to link the two independent libraries together. This is where the react-Redux binding library comes in
  4. Function: Connect Redux to React for state management.

React-redux – Basic use

steps

  1. Install NPM I react-redux

  2. use

    1. Create store, Reducer,action and so on as required by Redux
    2. Introduce providers, useSelector, and useDispatch from react-Redux to operate redux

API

Provider

  • Usage: Wraps directly on the root component.<Provider store={store}>
  • Benefit: Compared to React + Redux, this eliminates the need for every component to import a store

useSelector

  • Usage: Get public state

  • Benefits:

    • React + redux does not need to use store.getState()
    • When the state changes, it automatically updates
  • Format: const state = useSelector(Store data => the part you need)

useDispatch

  • Usage: Distribute action, modify data
  • Format:const dispatch = useDispatch(); dispatch(action)

Code: entry file index.js

import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import store from './store'
import { Provider } from 'react-redux'
ReactDom.render(
  <Provider store={store}>
    <App />
  </Provider>.document.getElementById('root'))Copy the code

Among the components that need to use and modify data:

import React from 'react'

import { useDispatch, useSelector } from 'react-redux'

export default function Uncle () {
  // Get the dispath modification data
  const dispatch = useDispatch()

  // Get data
  // state: data in redux
  const num = useSelector((state) = > {
    return state
  })
  
  / /...
Copy the code

6.action creator

Because when we create an Action, sometimes something is fixed and we may need to dispatch the Action in multiple places, we need to write the following long list each time:

dispatch({type: 'book/add'.payload: Romance of The Three Kingdoms})
dispatch({type: 'book/add'.payload: A Dream of Red Mansions})
Copy the code

All we need to do is define a function and pass in the parameters we want to change. This function, which receives some parameters that need to be modified and returns an Action, is called Action Creators in Redux.

After using Action Creators to create the Action, we wanted to modify the Store’s state so that it looked like this:

// Action Creators
const addAction = (payload) = > ({type: 'book/add', payload})

// Call the function to pass in the argument that needs to be changed
dispatch(addAction(Romance of The Three Kingdoms))
dispatch(addAction(A Dream of Red Mansions))
Copy the code

Likewise, action Creators can be divided into different modules as the project grows and demand increases:

├ ─ ─ the SRC ├ ─ ─ store # redux directory, usually call the store | ├ ─ ─ index. The js # define and store is derived. Which will import the reducer | └ ─ ─ the actions # modules action | ├ ─ ─ action1, js # module1The relevant action creator | ├ ─ ─ action2. Js # module2Relevant action creator | └ ─ ─ reducers # modules reducer | ├ ─ ─ reducer1. Js # module1The reducer | ├ ─ ─ reducer2. Js # module2The reducer | └ ─ ─ index. Js # reducer overall entrance, will import the reducer1, reducer2 ├ ─ ─ index. The js # entry documents of the project, will import and rendering App. Js ├ ─ ─ App. Js # root component, introducing the module1And the module2Component Exercises ─ modules1.Js # module1├ ─ module2.Js # module2componentCopy the code

7. Use of Action Type

A string action type is used in both Reducer and Action createor. If the project is too large and the content is too much, these strings are easy to write wrong, and it is not conducive to uniform modification. You can centralize action types to keep action types consistent across projects:

  1. Created in the Store directoryactionTypesDirectory orconstantsDirectory, centralized processing
  2. Use constants to store action types. Such as:
export const SET_NAME = 'user/setName'
export const SUB_MORE = 'money/subMore'
Copy the code

3. Replace these constants where action Types are used in your project to keep action Types consistent across your project

8. Story – thunk middleware

The execution time of the Redux middleware is between the Dispatching action and the reducer. Using middleware:

  • Dispatch (action) => Execute the middleware code => Reducer. Dispatch () is a dispatch wrapped by the middleware, but eventually the Redux library’s own dispatch method must be called

Redux-thunk – Basic use

The Redux-Thunk middleware can handle actions in the form of functions. Therefore, asynchronous action code can be executed in a functional action to complete asynchronous operations.

const action1 = async (dispatch) =>{
  const res = awaitAsynchronous action () dispatch({type: 'todos/add'.payload: res.data})
}

dispatch(action1)
Copy the code

Full directory structure

├ ─ ─ the SRC ├ ─ ─ store # redux directory, usually call the store | ├ ─ ─ index. The js # define and store is derived. Which will import the reducer | | └ ─ ─ the actions # modules action | ├ ─ ─ action1, js # module1The relevant action creator | ├ ─ ─ action2. Js # module2Relevant action creator | └ ─ ─ index. The js # combined action creator | | └ ─ ─ reducers # modules reducer | ├ ─ ─ reducer1. Js # module1The reducer | ├ ─ ─ reducer2. Js # module2The reducer | └ ─ ─ index. Js # reducer overall entrance, will import the reducer1, reducer2 | | └ ─ ─ actionTypes | ├ ─ ─ actionType1. Js # module1The actionType | ├ ─ ─ actionType2. Js # module2The actionType | ├ ─ ─ index. Js # entry documents of the project, will import and rendering App. Js ├ ─ ─ App. Js # root component, introducing the module1And the module2Component | | ─ ─ Pages ├ ─ ─ module1.Js # module1├ ─ module2.Js # module2componentCopy the code
NPM I redux: NPM I redux: NPM I redux thunk middleware: NPM I redux-thunk NPM I redux-devtools-extension -d: NPM I redux-devtools-extension -d: NPM I redux-devtools-extension -dCopy the code

Entry file index.js

import App from './App'
import ReactDOM from 'react-dom'
import './styles/index.css'

// Provider wraps the root component so that the App doesn't need to import each component into the store
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>.document.getElementById('root'))Copy the code

store/index.js

// applyMiddleware(middleware 1, middleware 2) uses middleware
import { createStore, applyMiddleware } from 'redux'

// composeWithDevTools() redux debugging tool
import { composeWithDevTools } from 'redux-devtools-extension'

// Thunk middleware dispath() can pass in functions to perform asynchronous requests
import thunk from 'redux-thunk'

// Reducer after the merge
import reducer from './reducers'

// Create a store. Pass the merged reducer
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))

export default store
Copy the code

reducer/index.js

Reducer of channel and newList components
import channel from './channel'
import newList from './newList'

// combineReducers merges all reducer
import { combineReducers } from 'redux'

const rootReducer = combineReducers({
  channel,
  newList
})
export default rootReducer
Copy the code