使用 useReducer 持久化 localStorage [英] Persist localStorage with useReducer

查看:51
本文介绍了使用 useReducer 持久化 localStorage的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 useState 的迷你购物车应用程序.我现在想重构由 useReducer 管理的应用程序状态,并继续使用 localStorage 保存数据.

我在弄清楚如何重构时遇到了麻烦,其中涉及到许多动人的部分.我如何重构 addToCartHandler 中的逻辑以在 ADD_TO_CART 案例中使用?从那里,我相信我能够找出 cartReducer 中其他情况的模式.谢谢.

完善应用的其余部分

cartReducer中细化reducer,并导出初始化函数和动作创建者.

const initialState = [];导出 const 初始值设定项 = (initialValue = initialState) =>JSON.parse(localStorage.getItem(localCart")) ||初始值;export const carReducer = (state, action) =>{开关(动作.类型){案例ADD_TO_CART":return state.find((item) => item.name === action.item.name)?state.map((item) =>item.name === action.item.name?{...物品,数量: item.quantity + 1}: 物品): [...state, { ...action.item, 数量: 1 }];案例REMOVE_FROM_CART":return state.filter((item) => item.name !== action.item.name);案例DECREMENT_QUANTITY"://如果数量为 1 从购物车中删除,否则减少数量return state.find((item) => item.name === action.item.name)?.quantity ===1?state.filter((item) => item.name !== action.item.name): state.map((item) =>item.name === action.item.name?{...物品,数量: item.quantity - 1}: 物品);案例CLEAR_CART":返回初始状态;默认:返回状态;}};export const addToCart = (item) =>({类型:ADD_TO_CART",物品});export const decrementItemQuantity = (item) =>({类型:DECREMENT_QUANTITY",物品});export const removeFromCart = (item) =>({类型:REMOVE_FROM_CART",物品});export const clearCart = () =>({类型:CLEAR_CART"});

Product.js 中通过 useContext 钩子获取购物车上下文并发送 addToCart 动作

import React, { useContext, useState } from react";import { CartContext } from "../CartProvider";import { addToCart } from "../cartReducer";const 项目 = () =>{const { dispatch } = useContext(CartContext);...const addToCartHandler = (产品) =>{调度(添加到购物车(产品));};...返回 (...);};

CartItem.js 获取并使用购物车上下文来分派减少数量或移除商品的动作.

import React, { useContext } from "react";import { CartContext } from "../CartProvider";从../cartReducer"导入 { decrementItemQuantity, removeFromCart };const CartItem = () =>{const { 购物车,调度 } = useContext(CartContext);const removeFromCartHandler = (itemToRemove) =>调度(removeFromCart(itemToRemove));const decrementQuantity = (item) =>dispatch(decrementItemQuantity(item));返回 (<>{cart.map((item, idx) => (<div className="cartItem"键={idx}><h3>{item.name}</h3><h5>数量:{item.quantity}{""}<跨度><按钮类型=按钮"onClick={() =>decrementQuantity(item)}><i>递减</i></span><h5>成本:{item.cost} </h5><button onClick={() =>removeFromCartHandler(item)}>Remove</button>

))}</>);};

App.js 通过上下文挂钩获取购物车状态和调度程序,并更新总商品和价格逻辑以说明商品数量.

import { CartContext } from "./CartProvider";import { clearCart } from "./cartReducer";导出默认函数 App() {const { 购物车,调度 } = useContext(CartContext);const clearCartHandler = () =>{调度(clearCart());};const { 项目,总计 } = 购物车.reduce(({items, total }, { cost,quantity }) =>({项目:项目+数量,总计:总计 + 数量 * 成本}),{ 项目:0,总计:0});返回 (

<h1>表情符号商店</h1><div className="products"><产品/>

<购物车项目/>

<h3>购物车中的商品:{items} |总成本:${total.toFixed(2)}<button onClick={clearCartHandler}>清除购物车</button>

);}

I have a mini shopping cart application that uses useState. I now want to refactor the application's state to be managed by useReducer and continue to persist data with localStorage.

I'm having trouble figuring out how to refactor, with the many moving pieces involved. How do I go about refactoring the logic within addToCartHandler to be instead used inside the ADD_TO_CART case? From there, I believe I'd be able to figure out the pattern for the other cases in the cartReducer. Thank you.

https://codesandbox.io/s/goofy-water-pb903?file=/src/App.js

解决方案

Use Context API to manage cart state

I would start by isolating your cart state and persistence to local storage to a react context provider. The context can provide the cart state and action dispatcher to the rest of the app, as well as persist the state to localStorage when the state updates using an effect. This decouples all of the state management from the app, the app need only consume the context to access the cart state and dispatch actions to update it.

import React, { createContext, useEffect, useReducer } from "react";
import { cartReducer, initializer } from "../cartReducer";

export const CartContext = createContext();

export const CartProvider = ({ children }) => {
  const [cart, dispatch] = useReducer(cartReducer, [], initializer);

  useEffect(() => {
    localStorage.setItem("localCart", JSON.stringify(cart));
  }, [cart]);

  return (
    <CartContext.Provider
      value={{
        cart,
        dispatch
      }}
    >
      {children}
    </CartContext.Provider>
  );
};

Wrap the app in the CartProvider in index.js

<CartProvider>
  <App />
</CartProvider>

Round out the rest of the app

In cartReducer refine the reducer, and export the initializer function and action creators.

const initialState = [];

export const initializer = (initialValue = initialState) =>
  JSON.parse(localStorage.getItem("localCart")) || initialValue;

export const cartReducer = (state, action) => {
  switch (action.type) {
    case "ADD_TO_CART":
      return state.find((item) => item.name === action.item.name)
        ? state.map((item) =>
            item.name === action.item.name
              ? {
                  ...item,
                  quantity: item.quantity + 1
                }
              : item
          )
        : [...state, { ...action.item, quantity: 1 }];

    case "REMOVE_FROM_CART":
      return state.filter((item) => item.name !== action.item.name);

    case "DECREMENT_QUANTITY":
      // if quantity is 1 remove from cart, otherwise decrement quantity
      return state.find((item) => item.name === action.item.name)?.quantity ===
        1
        ? state.filter((item) => item.name !== action.item.name)
        : state.map((item) =>
            item.name === action.item.name
              ? {
                  ...item,
                  quantity: item.quantity - 1
                }
              : item
          );

    case "CLEAR_CART":
      return initialState;

    default:
      return state;
  }
};

export const addToCart = (item) => ({
  type: "ADD_TO_CART",
  item
});

export const decrementItemQuantity = (item) => ({
  type: "DECREMENT_QUANTITY",
  item
});

export const removeFromCart = (item) => ({
  type: "REMOVE_FROM_CART",
  item
});

export const clearCart = () => ({
  type: "CLEAR_CART"
});

In Product.js get the cart context via a useContext hook and dispatch an addToCart action

import React, { useContext, useState } from "react";
import { CartContext } from "../CartProvider";
import { addToCart } from "../cartReducer";

const Item = () => {
  const { dispatch } = useContext(CartContext);

  ...

  const addToCartHandler = (product) => {
    dispatch(addToCart(product));
  };

  ...

  return (
    ...
  );
};

CartItem.js get and use the cart context to dispatch actions to decrement quantity or remove item.

import React, { useContext } from "react";
import { CartContext } from "../CartProvider";
import { decrementItemQuantity, removeFromCart } from "../cartReducer";

const CartItem = () => {
  const { cart, dispatch } = useContext(CartContext);

  const removeFromCartHandler = (itemToRemove) =>
    dispatch(removeFromCart(itemToRemove));

  const decrementQuantity = (item) => dispatch(decrementItemQuantity(item));

  return (
    <>
      {cart.map((item, idx) => (
        <div className="cartItem" key={idx}>
          <h3>{item.name}</h3>
          <h5>
            Quantity: {item.quantity}{" "}
            <span>
              <button type="button" onClick={() => decrementQuantity(item)}>
                <i>Decrement</i>
              </button>
            </span>
          </h5>
          <h5>Cost: {item.cost} </h5>
          <button onClick={() => removeFromCartHandler(item)}>Remove</button>
        </div>
      ))}
    </>
  );
};

App.js get both the cart state and dispatcher via context hook, and update the total items and price logic to account for item quantities.

import { CartContext } from "./CartProvider";
import { clearCart } from "./cartReducer";

export default function App() {
  const { cart, dispatch } = useContext(CartContext);

  const clearCartHandler = () => {
    dispatch(clearCart());
  };

  const { items, total } = cart.reduce(
    ({ items, total }, { cost, quantity }) => ({
      items: items + quantity,
      total: total + quantity * cost
    }),
    { items: 0, total: 0 }
  );

  return (
    <div className="App">
      <h1>Emoji Store</h1>
      <div className="products">
        <Product />
      </div>
      <div className="cart">
        <CartItem />
      </div>
      <h3>
        Items in Cart: {items} | Total Cost: ${total.toFixed(2)}
      </h3>
      <button onClick={clearCartHandler}>Clear Cart</button>
    </div>
  );
}

这篇关于使用 useReducer 持久化 localStorage的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆