# 解决状态撕裂

# useImmer

import { produce, Draft, freeze, castImmutable } from "immer";
import { useCallback, useState, SetStateAction } from "react";

// Draft 函数类型定义
export type DraftFunction<T> = (draft: Draft<T>) => void | T;

// 更新器类型
export type Updater<T> = ((draft: Draft<T>) => void) | T;

// Hook 返回类型
export type ImmerHook<T> = [T, (updater: Updater<T>) => void];

export function useImmer<T = unknown>(
  initialValue: T | (() => T)
): ImmerHook<T> {
  // 初始化状态
  const [val, updateValue] = useState<T>(() =>
    freeze(
      typeof initialValue === "function"
        ? (initialValue as () => T)()
        : initialValue,
      true
    )
  );

  // 更新函数实现
  const updateState = useCallback(
    (updater: Updater<T>) => {
      if (typeof updater === "function") {
        // 使用类型断言确保类型安全
        updateValue(
          produce(val, (draft: Draft<T>) => {
            return (updater as (draft: Draft<T>) => void)(draft);
          }) as T
        );
      } else {
        // 直接值更新
        updateValue(freeze(updater, true));
      }
    },
    [val]
  );

  return [val, updateState];
}

# jotai-immer

import React from "react";
import { atom, useAtom } from "jotai";
import { atomWithImmer } from "jotai-immer";

// 定义类型
interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

// 创建初始购物车状态
const cartAtom = atomWithImmer<CartItem[]>([
  { id: 1, name: "iPhone", price: 999, quantity: 1 },
  { id: 2, name: "iPad", price: 799, quantity: 1 },
]);

// 创建派生状态计算总价
const totalPriceAtom = atom((get) => {
  const cart = get(cartAtom);
  return cart.reduce((total, item) => total + item.price * item.quantity, 0);
});

const SimpleCart: React.FC = () => {
  const [cart, setCart] = useAtom(cartAtom);
  const [total] = useAtom(totalPriceAtom);

  const increaseQuantity = (id: number) => {
    setCart((draft) => {
      const item = draft.find((item) => item.id === id);
      if (item) {
        item.quantity += 1;
      }
    });
  };

  const decreaseQuantity = (id: number) => {
    setCart((draft) => {
      const item = draft.find((item) => item.id === id);
      if (item && item.quantity > 1) {
        item.quantity -= 1;
      }
    });
  };

  const removeItem = (id: number) => {
    setCart((draft) => {
      const index = draft.findIndex((item) => item.id === id);
      if (index !== -1) {
        draft.splice(index, 1);
      }
    });
  };

  return (
    <div className="max-w-md mx-auto p-4 text-black">
      <h1 className="text-2xl font-bold mb-4 text-[#fff]">Shopping Cart</h1>

      <div className="space-y-4">
        {cart.map((item) => (
          <div
            key={item.id}
            className="flex items-center justify-between bg-white p-4 rounded shadow"
          >
            <div>
              <h3 className="font-medium text-black">{item.name}</h3>
              <p className="text-black">${item.price}</p>
            </div>

            <div className="flex items-center space-x-2">
              <button
                onClick={() => decreaseQuantity(item.id)}
                className="px-2 py-1 bg-gray-200 rounded text-black hover:bg-gray-300"
              >
                -
              </button>
              <span className="text-black">{item.quantity}</span>
              <button
                onClick={() => increaseQuantity(item.id)}
                className="px-2 py-1 bg-gray-200 rounded text-black hover:bg-gray-300"
              >
                +
              </button>
              <button
                onClick={() => removeItem(item.id)}
                className="ml-4 text-red-500 hover:text-red-600"
              >
                Remove
              </button>
            </div>
          </div>
        ))}
      </div>

      <div className="mt-6 text-xl font-bold text-[#fff]">Total: ${total}</div>
    </div>
  );
};

export default SimpleCart;