React【异步逻辑createAsyncThunk(一)、createAsyncThunk(二)、性能优化、createSelector】(十二)

news2024/11/22 16:32:20

文章目录

异步逻辑

createAsyncThunk(一)

createAsyncThunk(二)

性能优化

createSelector


异步逻辑

//Product.js
const onAdd = () => {
        const name = nameRef.current.value
        // 触发添加商品的事件
        dispatch(addProduct({name}))
}

如果要让异步逻辑与Store交互,我们需要使用redux middleware。
Redux 有多种异步 middleware,每一种都允许你使用不同的语法编写逻辑。最常见的异步 middleware 是 redux-thunk ,它可以让你编写可能直接包含异步逻辑的普通函数。
Redux Toolkit 的 configureStore 功能默认自动设置 thunk middleware,我们推荐使用 thunk 作为 Redux 开发异步逻辑的标准方式。


Thunk 函数
在Thunk函数中我们可以编写异步逻辑的代码(例如 setTimeout 、Promise 和 async/await ),并且可以通过参数获取到dispatch,getState()。从而在异步操作执行后再diapacth action。
 

提示:
Thunk 通常写在 “slice” 文件中。

//slices/productSlice.js
import { createSlice } from '@reduxjs/toolkit'
//定义初始state
//list表示商品列表,isLoading表示是否为正在请求数据的状态
const initialState = { list: [] ,isLoading:false}
//创建slice
const slice = createSlice({
    //定义域名称
    name: 'product',
    //传入初始state
    initialState,
    //定义reducers
    reducers: {
        //这个reducer用来把商品数据存储到store中
        addProduct: (state, action) => {
               state.list.push(action.payload)
       },
        //这个reducer用来更改isLoading
        changeState:(state,action)=>{
            state.isLoading=action.payload
       }
   }
})
//导出action creator
export const { addProduct ,changeState} = slice.actions
//导出thunk函数
//addProductAsync为thunk函数的创建函数,它返回一个thunk函数
//返回的thunk函数中我们就可以编写异步代码了
export const addProductAsync = (payload) => (dispatch, getState) => {
    //触发action ,改变isLoading的状态
    dispatch(changeState(true))
    setTimeout(() => {
        dispatch(addProduct(payload))
        //触发action ,改变isLoading的状态
        dispatch(changeState(false))
   }, 3000)
}

//导出reducer
export default slice.reducer
//pages/Product.js
import React, { useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { addProductAsync } from '../slices/productSlice'
//引入thunk函数
export default function Product() {
    const nameRef = useRef()
    const {list:productList,isLoading} = useSelector(state => state.product)
    const dispatch = useDispatch()
    const onAdd = () => {
        //thunk函数的使用,跟普通的action creator的使用一样
    
        dispatch(addProductAsync({ name: nameRef.current.value }))
   }
    return (
        <div>
           我是商品页面<br />
           商品名:<input ref={nameRef} required /><br />
           {isLoading?<div>请求数据中... </div>:productList.map((item, index) => <li key={index}>
               商品名:{item.name}
            </li>)}
            
            <button onClick={onAdd}>新增商品</button>
        </div>
   );
}

createAsyncThunk(一)

 Redux Toolkit 的 createAsyncThunk API 生成 thunk,为你自动 dispatch 那些 "状态" action。

 createAsyncThunk 接收 2 个参数:
1、 参数一:将用作生成的 action type的前缀的字符串
2 、一个 “payload creator” 回调函数,它应该返回一个 Promise 或者其他数据

 //slices/productSlice.js
//使用createAsyncThunk创建thunk
//接收的第一个参数是action 的 type的前缀
//第二个参数是一个函数,用来返回payload
export const addProductPost = createAsyncThunk('product/addProductPost', (item)=>{
   return  new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(item)
       },3000)
   })
})

提示:
当调用 dispatch(addProductPost()) 的时候, addProductPost 这个 thunk 会
首先 dispatch 一个 action 类型为 'product/addProductPost/pending'
当异步代码执行完,返回的Promise resove后会dispatch 一个
action 类型为 product/addProductPost/fulfilled

 在组件中 dispatch thunk

import React, { useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { addProductPost } from '../slices/productSlice'
//引入thunk函数
export default function Product() {
    const nameRef = useRef()
    const {list:productList,isLoading} = useSelector(state => state.product)
    const dispatch = useDispatch()
    const onAdd = () => {
        //thunk函数的使用,跟普通的action creator的使用一样
    
        dispatch(addProductPost({ name: nameRef.current.value }))
   }
    return (
        <div>
           我是商品页面<br />
           商品名:<input ref={nameRef} required /><br />
           {isLoading?<div>请求数据中...
        </div>:productList.map((item, index) => <li key={index}>
               商品名:{item.name}
            </li>)}
            <button onClick={onAdd}>新增商品</button>
        </div>
   );

}

添加extraReducers
extraReducers可以监听createAsyncThunk创建的action被 dispatch。
 

//slices/productSlice.js
//创建slice
const slice = createSlice({
    //定义域名称
    name: 'product',
    //传入初始state
    initialState,
    //定义reducers
    reducers: {
        //这个reducer用来把商品数据存储到store中
        addProduct: (state, action) => {
            state.list.push(action.payload)
       },
        //这个reducer用来更改isLoading
        changeState:(state,action)=>{
            state.isLoading=action.payload
       }
   },
    //extraReducer设置createAsyncThunk创建的thunk被dispatch后的reducer处理器
    extraReducers(builder){
        builder
       .addCase(addProductPost.pending,(state,action)=>{
            state.isLoading=true
       })
       .addCase(addProductPost.fulfilled,(state,action)=>{
            state.isLoading=false
            state.list.push(action.payload)
       })
   }
})

createAsyncThunk(二)

 提示:
createAsyncThunk 自动生成 pending/fulfilled/rejected action 类型

//slices/productSlice.js
//创建获取商品数据的thunk
export const productListGet = createAsyncThunk('product/productListGet',
async () => {
    const data = await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve([{ name: '苹果' }, {name: '香蕉' }, { name: "蓝莓" }])
       }, 3000)
   })
    return data
})
extraReducers(builder) {
        builder
           .addCase(addProductPost.pending,(state, action) => {
                state.isLoading = true
           })
           .addCase(addProductPost.fulfilled, (state,action) => {
                state.isLoading = false
                state.list.push(action.payload)
           })
           .addCase(productListGet.pending, (state, action) => {
                state.isLoading = true
           })
           .addCase(productListGet.fulfilled, (state, action) => {

                return { list: action.payload, isLoading: false }
           })
           .addCase(productListGet.rejected, (state,action) => {
                state.isLoading=false
           })
   }
//pages/Product.js
import { addProductPost, productListGet } from '../slices/productSlice'
//组件挂载后请求商品数据
useEffect(() => {
        dispatch(productListGet())
   }, [])

提示:
Immer 让我们以两种方式更新状态:要么 更新 现有状态值,要么 return 一个新结果。
如果我们返回一个新值,它将用我们返回的任何内容完全替换现有状态。

性能优化

 React.memo()
React 的默认行为是当父组件渲染时,React 会递归渲染其中的所有子组件!

//pages/ProductChild.js
import React, { useEffect } from 'react';
function ProductChild() {
  
    useEffect(() => {
        console.log('子元素重新渲染')
   })
    return (
        <div>
           子元素
    </div>
   );
}
export default React.memo(ProductChild)

为了让子组件跳过没有必要的渲染,我们可以将 子组件包装在 React.memo() 中,这可以确保组件只有在 props 真正更改时才会重新渲染。
 

//pages/ProductChild.js
export default React.memo(ProductChild)

createSelector

 如果子组件中使用了useSelector来获取数据,也会存在一些不必要的渲染。

 提示:

一般情况下,只要我们dispatch 了 action,store发生了变更之后,那么传递给useSelector的选择器器就会被重新调用,如果选择器返回的结果跟原来的状态不一样,则组件重新被渲染。

import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
function ProductChild() {
     const list=useSelector(state=>state.product.list.filter(item=>item.name.length>2))
    useEffect(() => {
        console.log('子元素重新渲染')
   })
    return (
        <div>
           子元素
    </div>
   );
}
export default React.memo(ProductChild)

我们可以使用 createSelector 来定义有记忆的选择器。
 

//slices/productSlice.js
import { createSelector} from '@reduxjs/toolkit'
export const selectList= createSelector([state =>{    

    return state.product.list
} ], (list) => {
    console.log('重新计算list')
    return list.filter(item=>item.name.length>2)
})

createSelector函数可以接收N-1个输入函数,一个输出函数(最终的选择器),前面的N-1个输入函数的参数由调用输出函数的时候传入的参数决定,输出函数的参数由前面N-1个输入函数的返回值决定。只有当输出函数的参数发生了变更,输出函数才会被重新执行。

//pages/ProductChild.js
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import {selectList} from '../slices/productSlice'
function ProductChild() {
    // const list=useSelector(state=>state.product.list.filter(item=>item.name.length>2))
    const list=useSelector(selectList)
  
    useEffect(() => {
        console.log('子元素重新渲染')
   })
    return (
        <div>
           子元素
    </div>
   );
}
export default React.memo(ProductChild)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1194423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于.NET的强大文件格式开源转换工具

推荐一个非常强大、轻便的强大文件格式转换工具。 01 项目简介 一个基于.NET平台的开源文件格式转换工具&#xff0c;可以支持Windows 7/8/10等操作系统。安装后在右键菜单中出现 “File Converter” 项目&#xff0c;可以方便地通过右键菜单对选中文件进行格式转换&#xff…

HALCON的python下的使用方法(直接开发,不需要调用.hdev文件)

一、环境配置方法 基本要求&#xff1a; Python版本>3.8 ; Halcon版本 >20.11 1&#xff09;首先创建一个python版本大于3.8的基础环境 2&#xff09;然后查看自己的halcon的版本&#xff0c;在该环境下安装halcon 如图所示&#xff0c;版本是20110&#xff0c;执行以…

Java,多线程,线程的两种创建方式

首先是多线程的一些相关概念&#xff1a; 相关概念&#xff1a; 程序&#xff08;program&#xff09;&#xff1a;为完成特定任务&#xff0c;用某种语言编写的一组指令的集合。即指一段静态&#xff08;指不在执行中&#xff09;的代码。 进程&#xff08;process&#xf…

十分钟了解回归测试

1. 什么是回归测试&#xff08;Regression Testing&#xff09; 回归测试是一个系统的质量控制过程&#xff0c;用于验证最近对软件的更改或更新是否无意中引入了新错误或对以前的功能方面产生了负面影响&#xff08;比如你在家中安装了新的空调系统&#xff0c;发现虽然新的空…

34 mysql limit 的实现

前言 这里来看一下 我们常见的 mysql 分页的 limit 的相的处理 这个问题的主要是来自于 之前有一个需要处理 大数据量的数据表的信息, 将数据转移到 es 中 然后就是用了最简单的 “select * from tz_test limit $pageOffset, $pageSize ” 来分页处理 但是由于 数据表的数…

[autojs]逍遥模拟器和vscode对接

第一步&#xff1a;启动autojs服务 第二步&#xff1a;去cmd查看ip地址&#xff0c;输入ipconfig 第三步&#xff1a;打开逍遥模拟器中的sutojs-左上角- 连接电脑&#xff0c;然后输入WLAN或者其他ip也行&#xff0c;根据自己电脑实际情况确认 此时vscode显示连接成功。我们写…

上传ipa到appstore最简单的方法

假如使用原生xcode开发ios的app&#xff0c;可以使用xcode提交打包好的ipa文件到app store&#xff0c;但是假如使用hbuilderx或者apicloud等H5工具开发的app&#xff0c;假如没有mac电脑&#xff0c;是无法将ipa提交到app store的。 因为苹果开发者并不能在线上传ipa到app st…

数据结构-图的应用

最小生成树&#xff08;最小代价树&#xff09; 对于一个带权连通无向图G(V,E)&#xff0c;生成树不同&#xff0c;每棵树的权&#xff08;即树中所有边上的权值之和)也可能不同。设R为G的所有生成树的集合&#xff0c;若T为R中边的权值之和最小的生成树&#xff0c;则T称为G的…

SPI简介及FPGA通用MOSI模块实现

简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;通讯协议&#xff0c;是Motorola公司提出的一种同步串行接口技术。是一种高速、全双工、同步通信总线。在芯片中只占用四根管脚用来控制及数据传输。 优缺点&#xff1a; SPI通讯协…

VM17虚拟机设置网络,本地使用工具连接虚拟机

VM17虚拟机设置网络&#xff0c;本地使用工具连接虚拟机 下载及安装虚拟机不再说明&#xff0c;网络一堆教程。此处只对VM17设置网路及本地使用工具连接虚拟机操作&#xff0c;进行说明。 我下载的是VM17&#xff0c;网上有说VM16是较稳定的版本。想尝尝鲜&#xff0c;结果耗…

linux系统源码安装php5.6手把手教程

linux系统源码安装php5.6实用教程 1、下载php5.6安装包2、开始安装3、安装成功 1、下载php5.6安装包 wget http://mirrors.sohu.com/php/php-5.6.2.tar.gz在安装之前&#xff0c;我们需要安装php5.6编译时所依赖的软件包。如下&#xff1a; yum -y install gcc gcc-c lib2、开…

docker下的nginx代理转发到tomcat

多次尝试失败原因&#xff0c;修改nginx配置文件以后&#xff0c;需要./nginx.sh -s reload 下&#xff0c;之前一直不转发&#xff0c;好像完全没有跳转的意思&#xff0c;后来查了多篇文档&#xff0c;最简单的方法如下 docker 安装 nginx 和tomcat就不多说了&#xff0c;可…

一文6个步骤带你实现接口测试入门!

一、接口测试概述 1 什么是接口测试&#xff1a; 接口测试是测试系统组件间交互的一种测试。接口测试主要用于检测外部系统与系统之间&#xff0c;内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑…

CLion配置libtorch找不到xxx.dll

项目场景&#xff1a; 使用CLion配置libtorch时遇到该问题 问题描述 使用CLion配置libtorch时&#xff0c;CMakeLists.txt文件写完后&#xff0c;cmake也能成功&#xff0c;但是一旦运行代码就会报错找不到xxx.dll&#xff0c;比如找不到torch_cuda.dll或找不到c10.dll 原因分…

jsp基本表格和简单算法表格

基本表格&#xff1b; <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd…

java项目之果蔬经营平台系统(ssm框架)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的果蔬经营平台系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 管理员&#xff1a;首页、个人…

Android 12 S 系统开机流程分析-FirstStageMain(一)

开机有好几种方式启动&#xff0c;本文主要讲的是按Power键开机流程。 本文参考AOSP 12原生代码&#xff0c;链接为&#xff1a;AOSP 12 Searchhttp://aospxref.com/android-12.0.0_r3/ 目录 1. BootLoader加载 2. kernel启动 3. init进程启动 3.1 FirstStageMain 3.1.1…

Windows安装Docker(无网)

Windows安装Docker&#xff08;无网&#xff09; window无网安装Docker 1. 开启虚拟化功能 1. 开启window的虚拟化功能 方式一&#xff1a;直接在window的搜索框搜索 “启用或关闭windows功能”&#xff0c;就可以快捷进入【启用或关闭windows功能】页面 方式二&#xff1…

Gui基础使用之项目部署

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 越努力 &#xff0c;越幸运。 1.gui图形化界面的使用 1.1 前期准备 新建仓库&#xff0c;具体操作如下&#xff1a; 初始化readme文件&…

如何使用fiddler实现手机抓包,Filters过滤器!

一、Fiddler与其他抓包工具的区别 1、Firebug虽然可以抓包&#xff0c;但是对于分析http请求的详细信息&#xff0c;不够强大。模拟http请求的功能也不够&#xff0c;且firebug常常是需要“无刷新修改”&#xff0c;如果刷新了页面&#xff0c;所有的修改都不会保存&#xff1b…