日期:2024年5月19日

Reducer

在React的函数组件中,我们可以通过useState()来创建state。这种创建state的方式会给我们返回两个东西state和setState()。state用来读取数据,而setState()用来设置修改数据。但是这种方式也存在着一些不足,因为所有的修改state的方式都必须通过setState()来进行,如果遇到一些复杂度比较高的state时,这种方式似乎就变得不是那么的优雅。

举个例子,之前的《汉堡到家》的练习中,App.js中有一个state叫做cartData用来存储购物车数据。但是这个数据本身是比较复杂的,它包括了多个属性:

const [cartData, setCartData] = useState({
    items: [],
    totalAmount: 0,
    totalPrice: 0
});

同时购物车,也需要多个操作方法,像是添加食物、删除食物、清除购物车,而useState()只给我们提供了一个setCartData()方法,所以我们不得不在继续创建出三个不同的方法以实现出不同的功能:

const addItem = (meal) => {
    const newCart = {...cartData};
    if (newCart.items.indexOf(meal) === -1) {
        newCart.items.push(meal);
        meal.amount = 1;
    } else {
        meal.amount += 1;
    }
    newCart.totalAmount += 1;
    newCart.totalPrice += meal.price;
    setCartData(newCart);
};

const removeItem = (meal) => {
    const newCart = {...cartData};
    meal.amount -= 1;
    if (meal.amount === 0) {
        newCart.items.splice(newCart.items.indexOf(meal), 1);
    }
    newCart.totalAmount -= 1;
    newCart.totalPrice -= meal.price;
    setCartData(newCart);
};

const clearCart = () => {
    const newCart = {...cartData};
    newCart.items.forEach(item => delete item.amount);
    newCart.items = [];
    newCart.totalAmount = 0;
    newCart.totalPrice = 0;
    setCartData(newCart);
};

这三个函数定义在了App.js中,是操作cartData的三个函数。就这带来一些问题,首先,三个方法都是操作cartData的,但是它们被定义在App.js中和其他的函数混杂在了一起,维护起来并不方便。其次,三个方法并不是App.js自己调用,而是通过Context传递给其他组件调用,由于是三个函数所以我们不得不在Context中分别传递三个属性,也不方便。再有,如果后期我需要再添加新的功能,依然不可避免的要定义新的函数,并且修改Context。总之,就是各种不便利,这种不便还会随着项目复杂的提升而增加。

Reducer横空出世

为了解决复杂State带来的不便,React为我们提供了一个新的使用State的方式。Reducer横空出世,reduce单词中文意味减少,而reducer我觉得可以翻译为“当你的state的过于复杂时,你就可以使用的可以对state进行整合的工具”。当然这是个玩笑话,个人认为Reducer可以翻译为“整合器”,它的作用就是将那些和同一个state相关的所有函数都整合到一起,方便在组件中进行调用。

当然工具都有其使用场景,Reducer也不例外,它只适用于那些比较复杂的state,对于简单的state使用Reducer只能是徒增烦恼。但是由于初学,我们会先用一个简单的案例来对其进行演示,实际应用我们后边会以cartData作为演示。

State相同Reducer也是一个钩子函数,语法如下:

const [state, dispatch] = useReducer(reducer, initialArg, init);

它的返回值和useState()类似,第一个参数是state用来读取state的值,第二个参数同样是一个函数,不同于setState()这个函数我们可以称它是一个“派发器”,通过它可以向reducer()发送不同的指令,控制reducer()做不同的操作。

它的参数有三个,第三个我们暂且忽略,只看前两个。reducer()是一个函数,也是我们所谓的“整合器”。它的返回值会成为新的state值。当我们调用dispatch()时,dispatch()会将消息发送给reducer()reducer()可以根据不同的消息对state进行不同的处理。initialArg就是state的初始值,和useState()参数一样。

上代码:

import {useReducer, useState} from 'react';

const reducer = (state, action) => {
    switch(action.type){
        case 'add':
            return state + 1;
        case 'sub':
            return state - 1;
    }
};

function App() {

    const [count, countDispath] = useReducer(reducer,1);

    return (
        <div className="App">
            {count}

            <div>
                <button onClick={()=>countDispath({type:'sub'})}>-</button>
                <button onClick={()=>countDispath({type:'add'})}>+</button>
            </div>
        </div>
    );
}

export default App;

不知道代码你能不看懂,不过我也是不想分析了,不写了看视频吧。

修改购物车

修改App.js

...略...
const cartReducer = (state, action) => {
    const newCart = {...state};
    switch (action.type){
        case 'ADD_ITEM':
            if (newCart.items.indexOf(action.meal) === -1) {
                newCart.items.push(action.meal);
                action.meal.amount = 1;
            } else {
                action.meal.amount += 1;
            }
            newCart.totalAmount += 1;
            newCart.totalPrice += action.meal.price;
            return newCart;
        case 'REMOVE_ITEM':
            action.meal.amount -= 1;
            if (action.meal.amount === 0) {
                newCart.items.splice(newCart.items.indexOf(action.meal), 1);
            }
            newCart.totalAmount -= 1;
            newCart.totalPrice -= action.meal.price;
            return newCart;
        case 'CLEAR_CART':
            newCart.items.forEach(item => delete item.amount);
            newCart.items = [];
            newCart.totalAmount = 0;
            newCart.totalPrice = 0;
            return newCart;
        default:
            return state;
    }
};

const App = () => {

    ...略...

    const [cartData, cartDispatch] = useReducer(cartReducer, {
        items: [],
        totalAmount: 0,
        totalPrice: 0
    });

    ...略...

    return (
        <CartContext.Provider value={{...cartData, cartDispatch}}>
            <div>
                <FilterMeals onFilter={filterHandler}/>
                <Meals
                    mealsData={mealsData}
                />
                <Cart/>

            </div>
        </CartContext.Provider>
    );
};

export default App;

在其他组件中,需要操作购物车时,只需先获取CartContext然后通过ctx.cartDispath操作购物车:

const ctx = useContext(CartContext); // 加载context
ctx.cartDispatch({type:'CLEAR_CART'}); // 清空购物车
ctx.cartDispatch({type:'ADD_ITEM', meal:props.meal}); // 添加食物
ctx.cartDispatch({type:'REMOVE_ITEM', meal:props.meal}); // 删除食物
4.6 10 投票数
文章评分
订阅评论
提醒
guest

2 评论
最旧
最新 最多投票
内联反馈
查看所有评论
Chin
1 年 前

打卡

日本的
日本的
1 年 前

超哥np,信超哥,得永生

2
0
希望看到您的想法,请您发表评论x