React - Detailed explanation of the use of Redux Hooks

Redux Hooks

Introduction to Hooks in Redux

In previous redux development, in order to combine components with redux, we used connect in the react-redux library :

But this method must use higher-order functions to combine the returned higher-order components;

And you must write: mapStateToProps and mapDispatchToProps mapping functions, the specific usage is explained in the previous article;

Starting from Redux7.1, the Hook method is provided, and there is no need to write connect and corresponding mapping functions in function components.

useSelectorThe function is to map state to components :

Parameter 1: It is required to pass in a callback function, and the state will be passed to the callback function; the return value of the callback function is required to be an object, and the data to be used is written in the object. We can directly deconstruct the returned object. Get the data in the state that we want to use

const {
     
      counter } = useSelector((state) => {
     
     
  return {
     
     
    counter: state.counter.counter
  }
})

Parameter two: comparison can be made to determine whether the component should be re-rendered;

By default, useSelector will compare the two objects we return to see if they are equal ;

How can it be compared?

  • In the second parameter of useSelector, shallowEqualthe function in the react-redux library can be passed in for comparison
import {
     
      shallowEqual } from 'react-redux'

const {
     
      counter } = useSelector((state) => ({
     
     
  counter: state.counter.counter
}), shallowEqual)

That is, we must return two completely equal objects so as not to cause re-rendering;

useDispatchIt's very simple, just call the useDispatch Hook, you can directly get the dispatch function, and then use it directly in the component ;

const dispatch = useDispatch()

We can also get the current store object through useStore ( just understand, it is not recommended to directly operate the store object );


Use of Hooks in Redux

Let's use Redux's Hooks to implement a counter in the App component, and implement a case of modifying the message in the App subcomponent :

First we create a simple store

// store/modules/counter.js

import {
    
     createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
    
    
  name: "counter",
  initialState: {
    
    
    counter: 10,
    message: "Hello World"
  },
  reducers: {
    
    
    changeNumberAction(state, {
     
      payload }) {
    
    
      state.counter = state.counter + payload
    },
    changeMessageAction(state,  {
     
     payload }) {
    
    
      state.message = payload
    }
  }
})

export const {
    
     changeNumberAction, changeMessageAction } = counterSlice.actions

export default counterSlice.reducer
// store/index.js

import {
    
     configureStore } from "@reduxjs/toolkit";
import counterSlice from "./modules/counter"

const store = configureStore({
    
    
  reducer: {
    
    
    counter: counterSlice
  }
})

export default store

To use the react-redux library, you need to import Provider to wrap the App component.

import React from "react"
import ReactDOM from "react-dom/client"
import {
    
     Provider } from "react-redux"
import App from "./12_Redux中的Hooks/App"
import store from "./12_Redux中的Hooks/store"

const root = ReactDOM.createRoot(document.querySelector("#root"))

root.render(
  <Provider store={
    
    store}>
    <App/>
  </Provider>
)

Use useSelector and useDispatch in components to obtain and modify data in the store.

import React, {
    
     memo } from 'react'
import {
    
     useDispatch, useSelector } from 'react-redux'
import {
    
     changeMessageAction, changeNumberAction } from './store/modules/counter'

// 子组件Home
const Home = memo(() => {
    
    
  console.log("Home组件重新渲染")
  
  // 通过useSelector获取到store中的数据
  const {
    
     message } = useSelector((state) => ({
    
    
    message: state.counter.message
  }))

  // useDispatch获取到dispatch函数
  const dispatch = useDispatch()
  function changeMessage() {
    
    
    dispatch(changeMessageAction("Hello ChenYq"))
  }

  return (
    <div>
      <h2>{
    
    message}</h2>
      <button onClick={
    
    changeMessage}>修改message</button>
    </div>
  )
})


// 根组件App
const App = memo(() => {
    
    
  console.log("App组件重新渲染")
  
  // 通过useSelector获取到store中的数据
  const {
    
     counter } = useSelector((state) => ({
    
    
    counter: state.counter.counter
  }))

  // useDispatch获取到dispatch函数
  const dispatch = useDispatch()
  function changeNumber(num) {
    
    
    dispatch(changeNumberAction(num))
  }
  
  return (
    <div>
      <h2>当前计数: {
    
    counter}</h2>
      <button onClick={
    
    () => changeNumber(1)}>+1</button>
      <button onClick={
    
    () => changeNumber(-1)}>-1</button>

      <Home/>
    </div>
  )
})

export default App

Now we have used it in the component and modified the data in the store, but there is still a small problem ( performance optimization )

When the counter is modified in the App component, it is no problem that the App component will re-render; however, the Home component uses message and does not use counter, but it will also re-render; similarly, if the message is modified in the Home sub-component , the root component App will also be re-rendered; this is because by default useSelector is listening to the entire state, and when the state changes, the component will be re-rendered.

To solve this problem, we need to use the second parameter of useSelector to control whether re-rendering is needed. We only need to pass in the function in the react-redux library in the useSelector function. It will automatically perform a shallow comparison internally. shallowEqualWhen It will only be re-rendered when the data in the state used does change.

import React, {
    
     memo } from 'react'
import {
    
     useDispatch, useSelector, shallowEqual } from 'react-redux'
import {
    
     changeMessageAction, changeNumberAction } from './store/modules/counter'

// 子组件Home
const Home = memo(() => {
    
    
  console.log("Home组件重新渲染")

  const {
    
     message } = useSelector((state) => ({
    
    
    message: state.counter.message
  }), shallowEqual)

  const dispatch = useDispatch()
  function changeMessage() {
    
    
    dispatch(changeMessageAction("Hello ChenYq"))
  }

  return (
    <div>
      <h2>{
    
    message}</h2>
      <button onClick={
    
    changeMessage}>修改message</button>
    </div>
  )
})


// 根组件App
const App = memo(() => {
    
    
  console.log("App组件重新渲染")

  // 通过useSelector获取到store中的数据
  const {
    
     counter } = useSelector((state) => ({
    
    
    counter: state.counter.counter
  }), shallowEqual)

  // useDispatch获取到dispatch函数
  const dispatch = useDispatch()
  function changeNumber(num) {
    
    
    dispatch(changeNumberAction(num))
  }
  
  return (
    <div>
      <h2>当前计数: {
    
    counter}</h2>
      <button onClick={
    
    () => changeNumber(1)}>+1</button>
      <button onClick={
    
    () => changeNumber(-1)}>-1</button>

      <Home/>
    </div>
  )
})

export default App

Guess you like

Origin blog.csdn.net/m0_71485750/article/details/126876178