React Redux 中触发异步副作用
创始人
2024-02-26 17:21:03
0

React Redux 中触发异步副作用

一些基本的配置(这里使用 toolkit)可以在这篇笔记中找到:react-redux 使用小结,这里不多赘述。

触发副作用主流的操作方式有两种:

  1. 组件内操作

    适合只会在当前组件中触发的 API 操作

  2. 写一个 action creator 进行操作

    适合跨组件操作

二者并没有谁好谁坏的区别,主要还是依赖于业务需求

组件内触发

cart slice 的实现如下:

import { createSlice } from '@reduxjs/toolkit';
import { uiActions } from './ui-slice';const cartSlice = createSlice({name: 'cart',initialState: {items: [],totalQuantity: 0,totalAmount: 0,},reducers: {addItemToCart(state, action) {const newItem = action.payload;const existingItem = state.items.find((item) => item.id === newItem.id);state.totalQuantity++;if (!existingItem) {state.items.push({id: newItem.id,price: newItem.price,quantity: 1,total: newItem.price,title: newItem.title,});return;}existingItem.quantity++;existingItem.total += existingItem.price;},removeItemFromCart(state, action) {state.totalQuantity--;const id = action.payload;const existingItem = state.items.find((item) => item.id === id);if (existingItem.quantity === 1) {state.items = state.items.filter((item) => item.id !== id);return;}existingItem.quantity--;existingItem.total -= existingItem.price;},},
});export const cartActions = cartSlice.actions;export default cartSlice;

这个就相当于一个比较基本的购物车功能

UIslice:

import { createSlice } from '@reduxjs/toolkit';const uiSlice = createSlice({name: 'ui',initialState: {cartIsVisible: false,notification: null,},reducers: {toggle(state) {state.cartIsVisible = !state.cartIsVisible;},showNotification(state, action) {state.notification = {status: action.payload.status,title: action.payload.title,message: action.payload.message,};},},
});export const uiActions = uiSlice.actions;export default uiSlice;

这就是一些 UI 相关的操作。

app.js:

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { uiActions } from './store/ui-slice';// to avoid empty data being sent during initial mount
let isInitial = true;function App() {const dispatch = useDispatch();const { cartIsVisible, notification } = useSelector((state) => state.ui);const cart = useSelector((state) => state.cart);useEffect(() => {const sendCartData = async () => {dispatch(uiActions.showNotification({status: 'pending',title: 'sending',message: 'Sending cart data',}));const res = await fetch('some api here', {method: 'PUT',body: JSON.stringify(cart),});if (!res.ok) {throw new Error('Sending cart data failed.');}dispatch(uiActions.showNotification({status: 'success',title: 'Success...',message: 'Sent cart data successfully.',}));};if (isInitial) {isInitial = false;return;}sendCartData().catch((error) => {dispatch(uiActions.showNotification({status: 'error',title: 'Error...',message: 'Sending cart data failed.',}));});}, [cart, dispatch]);return (<>{notification && (notification.status}title={notification.title}message={notification.message}/>)}{cartIsVisible && });
}export default App;

实现上来说如下:

在这里插入图片描述

这里的一些流程:

|- useEffect
|   |- sendCartData(async call)
|   |   |- multiple dispatches

这个就是比较直接的操作,即在 useEffect 中调用异步操作,并且在 thenable 中进行结果的处理(这里就是 redux 的触发)。

custom action creator

之前直接利用 redux toolkit 写的 action 如下:

在这里插入图片描述

这里就是在 slice 外写了一个 customer action creator,也就是一个 thunk。实现方式,就像是在 toolkit 出来之前就要手动写很多的 action 这种感觉。

thunk 的定义如下:

a function that delays an action until later

一个延迟触发 action 的函数

当然,thunk 之类的生态圈已经发展了很多年了(在 toolkit 之前就有了),比如说比较老牌的 Redux Thunk,相对而言比较新一些的 redux-saga,它们都已经在市面上运行的比较稳定,而且周下载量都很大:

在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/fc54633351ef4525afbc715d51d82df5

很多时候可以根据业务需求进行配置,比如说项目比较简单,又没有现成的脚手架,那么现成的 toolkit 的功能说不定就够了。如果业务需求比较复杂,那么可以考虑使用 thunk 或是 saga。

这里因为是对于购物车的操作,所以 custom action creator 会放在 cart slice 中:

import { createSlice } from '@reduxjs/toolkit';
import { uiActions } from './ui-slice';const cartSlice = createSlice({name: 'cart',initialState: {items: [],totalQuantity: 0,totalAmount: 0,},reducers: {addItemToCart(state, action) {const newItem = action.payload;const existingItem = state.items.find((item) => item.id === newItem.id);state.totalQuantity++;if (!existingItem) {state.items.push({id: newItem.id,price: newItem.price,quantity: 1,total: newItem.price,title: newItem.title,});return;}existingItem.quantity++;existingItem.total += existingItem.price;},removeItemFromCart(state, action) {state.totalQuantity--;const id = action.payload;const existingItem = state.items.find((item) => item.id === id);if (existingItem.quantity === 1) {state.items = state.items.filter((item) => item.id !== id);return;}existingItem.quantity--;existingItem.total -= existingItem.price;},},
});// custom action creator
export const sendCartData = (cart) => {return async (dispatch) => {dispatch(uiActions.showNotification({status: 'pending',title: 'sending',message: 'Sending cart data',}));const sendRequest = async () => {const res = await fetch('some api here', {method: 'PUT',body: JSON.stringify(cart),});if (!res.ok) {throw new Error('Sending cart data failed.');}};try {await sendRequest();dispatch(uiActions.showNotification({status: 'success',title: 'Success...',message: 'Sent cart data successfully.',}));} catch (e) {dispatch(uiActions.showNotification({status: 'error',title: 'Error...',message: 'Sending cart data failed.',}));}};
};export const cartActions = cartSlice.actions;export default cartSlice;

app.js 中的代码:

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { sendCartData } from './store/cart-slice';let isInitial = true;function App() {const dispatch = useDispatch();const { cartIsVisible, notification } = useSelector((state) => state.ui);const cart = useSelector((state) => state.cart);useEffect(() => {if (isInitial) {isInitial = false;return;}dispatch(sendCartData(cart));}, [cart, dispatch]);return (<>{notification && (notification.status}title={notification.title}message={notification.message}/>)}{cartIsVisible && });
}export default App;

可以看到,本质上来说,custom action creator 中的代码基本上就是将组件内部的代码移到了另一个地方进行中心化处理。这样的优点比较多,比如说 e-commerce 的项目来说,在每个商品详情页面中可以进行购物车的操作,也可以单独到购物车的页面进行操作,或是鼠标悬浮到购物车,都会弹出一个下拉框进行简单的操作等。

这样粗略一算就会有 3 个地方都会用到同样的 API 调用,同样的 UI 提示,这个情况下就可以考虑将这部分的代码封装到 custom action creator 中,减少重复代码。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...