Help and docs
Sign in

demo/acme-web code browser

main 72fe66b
TypeScript · 48 lines · 1.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { useSyncExternalStore } from "react";
import type { Money, Product } from "../api/client";
import { addMoney } from "../lib/money";

export type CartLine = { product: Product; quantity: number };

let lines: CartLine[] = [];
const listeners = new Set<() => void>();

function emit() {
  listeners.forEach((l) => l());
}

export const cart = {
  add(product: Product) {
    const line = lines.find((l) => l.product.id === product.id);
    lines = line
      ? lines.map((l) => (l === line ? { ...l, quantity: l.quantity + 1 } : l))
      : [...lines, { product, quantity: 1 }];
    emit();
  },
  remove(productID: string) {
    lines = lines.filter((l) => l.product.id !== productID);
    emit();
  },
  clear() {
    lines = [];
    emit();
  },
  total(currency: string): Money {
    return addMoney(
      currency,
      lines.map((l) => ({ currency, minor: l.product.price.minor * l.quantity })),
    );
  },
};

// useCart re-renders its component whenever the cart changes.
export function useCart() {
  useSyncExternalStore(
    (l) => {
      listeners.add(l);
      return () => listeners.delete(l);
    },
    () => lines,
  );
  return { lines, ...cart };
}