1. The story of the core

1.1 Redux introduces

JavaScript state containers, which provide testable state management containers: JavaScript object state: Each DOM element has a state, such as: A button, the “show” or “hide” of a button is the state. The state is eventually abstracted as data stored in an object, which is called a state container. The data in the container corresponds to the state of the DOM element.

const state = {
    modelOpen: "yes".btnClicked: "no".btnActiveClass: 'active'.page: 5.size: 10
}
Copy the code

Redux is a JavaScript state container. Redux also provides a scientific way to manage state, making it easier and maintainable.

1.2 Redux core concepts and workflow

In the application of Redux, all states are stored in the store, and the view cannot directly operate the states in the store. Unless actions are triggered, the actions will be received by reducer, and the reducer will handle the states differently based on the type attribute value of the actions. When reducer processed the state, it returned the state to store by return value to update the state in store. When the state in store was updated, we knew the state was updated by subscribe method, and then synchronized the state in view.

  • createStoreThe reducer function is used to create a store state container, and the return value is the store object that stores the state. The first parameter is the reducer function,
  • reducerThe reducer function stores state from the store, and the reducer returns what is stored in the store. Reducer has two parameters. The first parameter is state and the state stored in the store, and the second parameter is action and received action objects. Store state is handled differently internally depending on the value of the Action’s type attribute
  • getState()Used to get the state stored in the store
  • subscribe()Subscribe is used to subscribe to the store. When the state of the store changes, a callback to subscribe is passed in. This method is usually used to get the latest state of the store to synchronize the view
  • dispatch()Used to trigger an action, the first argument receives an Action object. When you want to trigger an action in the view, you must call Dispatch to trigger the action

2.React + Redux

2.1 Problems When Redux is Not used in React

In React, the data flow between components is one-way. The top component can transmit data to the lower component through the props property, while the lower component cannot transmit data to the upper component. To enable the lower component to modify data, the upper component needs to transmit data modification methods to the lower component. It becomes increasingly difficult to pass data between components.

2.2 Benefits of adding Redux to the React project

Using Redux to manage data, since store is independent of components, makes data management independent of components and solves the problem of passing data from component to component. Each component can fetch and modify data directly from the Store. Use react-redux to combine React and Redux better

  1. The provider component places stores within reach of global components
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { Provider } from 'react-redux';
import { store } from './store';

ReactDOM.render(
  // The provider component places stores where global components can reach them
  <Provider store={store}><App/></Provider>.document.getElementById('root'));Copy the code
  1. The Connect method helps simplify getting state from the Store and the Dispatch method, subscribing to state changes in the Store, and re-updating components
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '.. /store/actions/counter.actions';

function Counter ({count, increment, decrement, increment_async}) {
  return <div>
    <button onClick={()= > increment_async(20)}>+</button>
    <span>{count}</span>
    <button onClick={()= > decrement(5)}>-</button>
  </div>
}

// 1. The connect method will help us subscribe to the store and will help us re-render the component when the state in the Store changes
The connect method lets us get the state in the store and map the state to the component through the component's props property
// 3. The connect method lets us get the Dispatch method

// mapStateToProps maps state to props as the first parameter of connect
const mapStateToProps = state= > ({
  count: state.counter.count
});

// mapDispatchToProps maps dispatch to props as the second parameter of connect
const mapDispatchToProps = dispatch= > bindActionCreators(couterActions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(Counter);
Copy the code
// src/store/actions/counter.actions.js
export const increment = payload= > ({type: 'increment', payload})
export const decrement = payload= > ({type: 'decrement', payload})
Copy the code

3. The story middleware

3.1 What is middleware

Middleware is a function that allows us to extend redux applications. Expansion and enhancement are reflected in the ability to deal with actions. Previously, actions were directly processed by reducer after they were received by reducer, but after they were added to middleware, actions were preferentially processed by middleware, and then sent to Reducer for further processing after intermediate actions were processed.

3.2 Middleware Redux workflow is added

4. Develop Redux middleware

4.1 Develop the template code of middleware

Such as:

// middleware/logger.js
export default store => next= > action= > {
    console.log(store)
    console.log(action)
    next(action) Next must not forget to call, otherwise other middleware will not execute, and Reducer will not execute
}
Copy the code

4.2 Registering Middleware

Middleware only takes effect in the Redux workflow once it is registered after development

import { createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'

createStore(reducer, applyMiddleware(
    logger
))
Copy the code

5. Redux common middleware

5.1 story – thunk

Function: NPM install redux-thunk 5.1.2 Introducing redux-thunk import thunk from ‘redux-thunk’ 5.1.3 Registering redux-thunk

import { applyMiddleware } from 'redux'
createStore(rootReducer, applyMiddleware(thunk))
Copy the code

5.1.4 Using the Redux-Thunk middleware

With the introduction of redux-thunk, actionCreators can no longer just return action objects, but a function that performs an asynchronous operation from that function, and then dispatks another action that delivers the results of the asynchronous operation to the Reducer
const loadPosts = () = > async dispatch => {
    const posts = await axios.get('/api/posts').then(response= > response.data)
    dispatch({ type: LOADPOSTSUCCESS, payload: posts })
}
Copy the code

5.2 story – saga

What it does: Like Redux-Thunk, asynchronous code can be added to the Redux workflow, but more powerful, allowing asynchronous operations to be taken out of the Action Creator file and placed in a separate file. Make project code more maintainable. 5.2.1 NPM install Redux-saga 5.2.2 Introducing && Creating redux-Saga middleware

import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = creaeSagaMiddleware()
Copy the code

5.2.3 Register sagaMiddleware createStore(Reducer, applyMiddleware(sagaMiddleware)) 5.2.4 Use Saga to receive Actions and perform asynchronous operations

// src/store/sagas/post.saga.js
import { takeEvery, put } from 'redux-saga/effects'

function* load_posts () {
    const { data } = yeild axios.get('/api/posts.json')
    // Put is used to trigger another ACION like Dispatch. When the asynchronous operation is completed, another action needs to be triggered to deliver the result of the asynchronous operation to the reducer
    yeild put(load_posts_success(data))
}

export default function* postSaga () {
    The first argument to takeEvery is a string of type action. The second argument is the method to execute when the type is received. It can be a function name (the function is defined above), or it can be written to
    yeild takeEvery(LOAD_POSTS, load_posts)
}
Copy the code

5.2.5 Starting Saga Saga must be started before saga can be added to redux workflow

import postSaga from './store/sagas/post.saga'
sagaMiddleware.run(postSaga)
Copy the code

5.3 story – the actions

Use redux-Actions to simplify the actions and Reducer process

import { createAction } from 'redux-actions'

const increment = createAction('increment')
const decrement = createAction('decrement')
Copy the code

Is equivalent to:

const increment = payload= > ({type: 'increment', payload})
const decrement = payload= > ({type: 'decrement', payload})
Copy the code

Even the parameters were delivered by react-action. The reducer can directly obtain the parameters through action.payload

5.3.3 create Reducer

import { handleActions as createReducer } from 'redux-actionss'
import { increment, decrement } from '.. /actions/counter.action'

const initialState = {count: 0}
const counterReducer = createReducer({
    [increment]: (state, action) = > ({count: state.count + 1}),
    [decrement]: (state, action) = > ({count: state.count - 1})
}, initialState)
export default counterReducer
Copy the code

Compared to the original Switch… Case writing, reducer writing has become much simpler!