1.1 Redux introduces

Article source: Pull hook big front salary boot camp

JavaScript state containers that provide predictable state management

1.2 Redux core concepts and processes

Store: A container for storing state, JavaScript objects

View: An HTML page

Actions: object describing what Actions are taken on state

Reducers: Function that manipulates the state and returns a new state

<! DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial-scale =1.0"> <title>Redux</title> </head> <body> <button id="minus">-</button> <span id="count">0</span> <button id="plus">+</button> <script src="./redux.min.js"></script> <script> // 3. Store default state const initialState = {count: 0} // 2. Function Reducer (state = initialState, action) {switch (action.type) {case 'increment': return {count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; Const store = redux.createstore (reducer); Action Const increment = {type: 'increment'} const decrement = {type: 'decrement'} // 5. Document.getelementbyid ('minus').addeventListener ('click', function () {// 6. Rement action Store.dispatch (Decrement)}) document.getelementByID ('plus').addeventListener ('click', function () { // 6. Get dispatch Trigger Action store.dispatch(increment)}) // Get Store {dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, Symbol(Observable): ƒ} console.log(store) Store.subscribe (() => {console.log(store.getState()) document.getelementById ('count').innerhtml = store.getState().count }); </script> </body> </html>Copy the code

// Create a store object

const store = Redux.createStore(reducer);

// Get the dispatch trigger action

store.dispatch(increment)

Create the reducer function

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      returnstate; }}Copy the code

// Get the state stored in store

store.getState()

// Changes to subscription data

store.subscribe(() = > {
  console.log(store.getState())
});
Copy the code

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 pass data to the lower component through the Props property, but the lower component cannot pass data to the upper component. To enable the lower-level component to modify data, the upper-level component needs to pass the method of modifying data to the lower-level component. As projects get larger, it becomes more difficult to transfer 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

npm install redux react-redux

2.3 Redux workflow

The component triggers an Action through the Dispatch method

The Store accepts the actions and distributes them to the Reducer

Reducer changes the state based on the Action type and returns the changed state to the Store

The component subscribes to state in the Store, and state updates in the Store are synchronized to the component

2.4 Procedure for Using Redux

Create the store

// src/store/index.js
import { createStore } from 'redux'
import reducer from './reducers/counter.reducer'
export const store = createStore(reducer)
Copy the code

Use store in the root component

import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './components/Counter'
import { Provider } from 'react-redux'
import {store} from './store'
/** * react-redux * Provider * connect */

ReactDOM.render(
  // With the Provider component, store is placed where global components can reach it
  <Provider store={store}>
    <Counter />
  </Provider>.document.getElementById('root'));Copy the code

Create the reducer

// src/store/reducers/counter.reducer.js
import { DECREMENT, INCREMENT } from ".. /count/counter.const";

const initialState = {
  count: 0
}

export default function reducer (state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + 1 };
    case DECREMENT:
      return { count: state.count - 1 };
    default:
      returnstate; }}Copy the code
// src/store/count/counter.const.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
Copy the code

Use connect in the component to accept state and dispatch from the store

The connect method takes two arguments and returns a higher-order component.

The first argument to the connect method is the mapStateToProps method, which passes the state of the store to the props of the component. The mapStateToProps method, which takes the state argument, returns an object, which is passed to the component as follows:

const mapStateToProps = (state) = > ({
  count: state.count,
  a: 'a'.// How to define that a component can or can get a property
})
Copy the code

The second argument to the connect method is the mapDispatchToProps method, which passes the dispatch from the store to the props of the component. The return value of the mapDispatchToProps method is an object, Methods in the object can use Dispatch, and the methods in the object are passed to the component as follows:

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

Alternatively, we can create the action function with bindActionCreators in Redux:

import {bindActionCreators} from 'redux'

// bindActionCreators will return an object
const mapDispatchToProps = dispatch= > (
  / / deconstruction. bindActionCreators({ increment () {return { type: 'increment'}
    },
    decrement () {
      return { type: 'decrement'}
    }
  }, dispatch)
)

Copy the code

Or written

const mapDispatchToProps = dispatch= > bindActionCreators({
    increment () {
      return { type: 'increment'}
    },
    decrement () {
      return { type: 'decrement'}
    }
  }, dispatch)

Copy the code

You can also extract the first parameter of bindActionCreators:

import * as counterActions from '.. /store/actions/counter.actions'
const mapDispatchToProps = dispatch= > bindActionCreators(conterActions, dispatch)
Copy the code
// src/store/actions/counter.actions.js
import { DECREMENT, INCREMENT } from ".. /count/counter.const"

export const increment = () = > ({type: INCREMENT})
export const decrement = () = > ({type: DECREMENT})

Copy the code

The connect method accepts mapStateToProps and mapDispatchToProps, returns a higher-order component, and passes in the Counter component for export:

export default connect(mapStateToProps, mapDispatchToProps)(Counter)

The final component code is as follows

// src/components/Counter.js
import React from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import * as counterActions from '.. /store/actions/counter.actions'

function Counter ({count, increment, decrement}) {
  return (
    <div>
      <button onClick={decrement}>-</button>
      <span>{count}</span>
      <button onClick={increment}>+</button>
    </div>)}// 1. Connect will help us subscribe to the store and re-render the component when the state in the Store changes
// 2. The connect method lets us get the state in the store and map the state to the component through the props property of the component
// 3. The connect method lets us get the Dispatch method

const mapStateToProps = (state) = > ({
  count: state.count,
  a: 'a'.// How to define that a component can or can get a property
})

const mapDispatchToProps = dispatch= > bindActionCreators(counterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter)

Copy the code

Pass parameters for action

Passing parameters

<button onClick={() => increment(5)}> + 5</button>

Accept the parameters and pass reducer

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

Reducer process the data received

export default function reducer (state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + action.payload };
    case DECREMENT:
      return { count: state.count - action.payload };
    default:
      returnstate; }}Copy the code

Split reducer

The more states in the store, the more switch branches in the Reducer will be, which is not beneficial to maintenance. Therefore, we need to split the Reducer and merge each small reducer using the combineReducers provided by the Reducer

// src/store/reducers/root.reducer.js
import {combineReducers} from 'redux'
import CounterReducer from './counter.reducer'
import ModalReducer from './modal.reducer'

// { counter: { count: 0 }, modal: { show: false } }
export default combineReducers({
  counter: CounterReducer,
  modal: ModalReducer
})

Copy the code
const mapStateToProps = (state) = > ({
  count: state.counter.count,
})
Copy the code
const mapStateToProps = state= > ({
  showStatus: state.modal.show
})
Copy the code
// src/store/index.js
import { createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'

createStore(reducer, applyMiddleware(
  logger
))

const logger = store= > next= > action= > {
  console.log(store)
  console.log(action)
  next(action) // Don't forget to call next(action)
}
export default logger
// If more than one middleware is registered, the order of execution of the middleware is the order of registration, for example:
createStore(reducer, applyMiddleware(
  logger,
  test
))
// The order of execution is logger middleware first, then test middleware.
// If the end in the middleware does not call next(action), the entire process is stuck at this point and cannot be executed further

Copy the code