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

文章目录

异步逻辑

createAsyncThunk(一)

createAsyncThunk(二)

性能优化

createSelector


异步逻辑

javascript 复制代码
//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" 文件中。

javascript 复制代码
//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
javascript 复制代码
//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 或者其他数据

javascript 复制代码
 //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

javascript 复制代码
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。

javascript 复制代码
//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 类型

javascript 复制代码
//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
           })
   }
javascript 复制代码
//pages/Product.js
import { addProductPost, productListGet } from '../slices/productSlice'
//组件挂载后请求商品数据
useEffect(() => {
        dispatch(productListGet())
   }, [])

提示:

Immer 让我们以两种方式更新状态:要么 更新 现有状态值,要么 return 一个新结果。

如果我们返回一个新值,它将用我们返回的任何内容完全替换现有状态。

性能优化

React.memo()

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

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

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

javascript 复制代码
//pages/ProductChild.js
export default React.memo(ProductChild)

createSelector

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

提示:

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

javascript 复制代码
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 来定义有记忆的选择器。

javascript 复制代码
//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个输入函数的返回值决定。只有当输出函数的参数发生了变更,输出函数才会被重新执行。

javascript 复制代码
//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)
相关推荐
转世成为计算机大神24 分钟前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
sinat_3842410928 分钟前
使用 npm 安装 Electron 作为开发依赖
服务器
qq_327342731 小时前
Java实现离线身份证号码OCR识别
java·开发语言
沉默璇年1 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder1 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727571 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
SoaringHeart2 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
Kkooe2 小时前
GitLab|数据迁移
运维·服务器·git
会发光的猪。2 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list