在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}); // 删除食物
打卡
超哥np,信超哥,得永生