react课程16-继续使用Redux构建pizza项目

Last updated on September 20, 2024 pm

由于这部分内容比较简单,就简略记录一下重点叭(Redux快被我忘得一干二净了🥲🥲)

(1)useSelector

在前面如果我们想要获取store里的状态,都是用useSelector在相应的组件函数里直接获取。但是现在发现其实更推荐在Slice文件中集中地写入selector函数,如下所示:

1
2
3
4
5
6
7
export const getCart = (state) => state.cart.cart;

export const getTotalCartQuantity = (state) =>
state.cart.cart.reduce((sum, item) => sum + item.quantity, 0);

export const getTotalCartPrice = (state) =>
state.cart.cart.reduce((sum, item) => sum + item.totalPrice, 0);

这样的话如果将来需要改变某个状态名字,就可以只在这里修改一次了。

(2)??运算符

例如:

1
2
export const getCurrentQuantityById = (id) => (state) =>
state.cart.cart.find((item) => item.pizzaId === id)?.quantity ?? 0;

??空值合并运算符(Nullish Coalescing Operator)。它用于在左侧表达式为 nullundefined 时,返回右侧的值。

(3)caseReducers

在购物车中实现增加和减少pizza数量的时候遇到了老问题,就是pizza的数量会变成负值。我试图解决这个问题,于是写了一行代码:if (!quantity) return;这样如果pizza的数量减到0之后就不会呈现这个pizza,也就无法再使它的数量减下去了。然而随后又遇到了另一个问题,就是把所有pizza数量减为0的时候,没有展示预设的空购物车界面,也就是说cart的长度不为0,于是我得到cartprice,用它来决定是否展示空购物车界面。

但是很明显不是正确的方法,因为在store里,这时候仍旧有这些quantity为0的pizza的元素,只是个数为0而已,最好的办法是删除这些元素。于是可以在slice里的dcrease函数中添加代码:

if (item.quantity === 0) cartSlice.caseReducers.deleteItem(state, action);

(4)creatAsyncThunk

createAsyncThunk 是 Redux Toolkit 提供的一个功能,用来简化在 Redux 中处理异步逻辑的操作。它自动生成相应的 action 类型并管理异步请求的生命周期,如 pendingfulfilledrejected 状态,减少了手动书写异步逻辑的工作量。

createAsyncThunk 的优点:

  1. 自动生成 action:无需手动编写 pendingfulfilledrejected action,简化了代码。
  2. 内置的错误处理机制:可以使用 rejectWithValue 返回自定义的错误信息。
  3. 统一的异步逻辑管理:通过 extraReducers 来处理不同状态的逻辑,使代码更加可读。

下列这段代码可以和课程13中的Thunk做对比。

首先,传入动作类型和需要执行的异步函数,一旦action被派遣,这里的函数就会被执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export const fetchAddress = createAsyncThunk(
'user/fetchAddress',
async function () {
// 1) We get the user's geolocation position
const positionObj = await getPosition();
const position = {
latitude: positionObj.coords.latitude,
longitude: positionObj.coords.longitude,
};

// 2) Then we use a reverse geocoding API to get a description of the user's address, so we can display it the order form, so that the user can correct it if wrong
const addressObj = await getAddress(position);
const address = `${addressObj?.locality}, ${addressObj?.city} ${addressObj?.postcode}, ${addressObj?.countryName}`;

// 3) Then we return an object with the data that we are interested in
return { position, address };
},
);

(5)value和defaultValue

value

  • 受控组件:当使用 value 属性时,表单元素的值完全由 React 组件的状态控制。这种方式称为“受控组件”(controlled component)。
  • 实时同步value 需要与组件的状态保持同步,当输入发生变化时,必须通过状态更新来改变值。通常搭配 onChange 事件来更新状态。

defaultValue

  • 非受控组件:使用 defaultValue 时,表单元素的初始值仅在组件首次渲染时设置,之后它的值不再由 React 控制。这种方式称为“非受控组件”(uncontrolled component)。
  • 仅初始化defaultValue 仅用于设置元素的初始值,表单控件在用户输入后会自己管理其值,React 不再负责后续的变化。

(6)fetcher

使用 useFetcher 钩子来处理非导航相关的表单提交,适合需要局部更新或是处理背景请求的场景。fetcher.Form 创建了一个 <form>,并且使用 PATCH 方法提交数据。这种方式让 react-router 能够在不刷新整个页面的情况下局部提交数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { useFetcher } from 'react-router-dom';
import Button from '../../ui/Button';
import { updateOrder } from '../../services/apiRestaurant';

function UpdateOrder({ order }) {
const fetcher = useFetcher();

return (
<fetcher.Form method="PATCH" className="text-right">
<Button type="primary">Make priority</Button>;
</fetcher.Form>
);
}

export default UpdateOrder;

export async function action({ request, params }) {
// console.log('update');
const data = { priority: true };
await updateOrder(params.orderId, data);
return null;
}

在没有数据并且 fetcher 处于空闲 (idle) 状态时,通过 fetcher.load('/menu') 来发起请求加载菜单数据。

1
2
3
4
5
6
useEffect(
function () {
if (!fetcher.data && fetcher.state === 'idle') fetcher.load('/menu');
},
[fetcher.state, fetcher.data],
);

(7)join

join() 函数主要用于将一个列表或数组中的元素连接成一个字符串。它通常用于将多个字符串拼接在一起,中间插入指定的分隔符。

array.join(separator);

  • **separator**:可选参数。指定分隔数组元素的字符。如果未指定,默认使用逗号 , 作为分隔符。
  • 在没有指定 separator 时,默认使用逗号 , 分隔。

react课程16-继续使用Redux构建pizza项目
http://example.com/2024/09/19/react课程16-继续使用Redux构建pizza项目/
Author
Yaodeer
Posted on
September 19, 2024
Licensed under