Skip to main content

Overview

The useUserStore is a Zustand store that manages user-related state including shipping information, payment details, and order history. It integrates with Auth0 to hydrate user data and persists all data to local storage under the key user-checkout-storage. Store Location: src/store/useUserStore.js

State Shape

shipping
object
required
Shipping information for the current user
payment
object
required
Payment method information
orders
array
required
Array of completed orders. Each order contains items, total, and timestamp.

Methods

setShipping

Updates shipping information. Merges new data with existing shipping state.
data
object
required
Partial shipping object with fields to update. Can include fullName, address, and/or city.
const setShipping = useUserStore((state) => state.setShipping);

setShipping({
  fullName: "John Doe",
  address: "123 Main St",
  city: "San Francisco"
});

// Partial updates are supported
setShipping({ city: "Los Angeles" });

setPayment

Updates payment information. Merges new data with existing payment state.
data
object
required
Partial payment object with fields to update. Can include cardNumber, expiryDate, and/or cvc.
const setPayment = useUserStore((state) => state.setPayment);

setPayment({
  cardNumber: "4111111111111111",
  expiryDate: "12/25",
  cvc: "123"
});

hydrateFromAuth0

Populates user data from Auth0 user object. Currently hydrates the fullName field from Auth0’s name property if shipping name is empty.
user
object
required
Auth0 user object containing user profile information
const hydrateFromAuth0 = useUserStore((state) => state.hydrateFromAuth0);
const { user } = useAuth0();

// Usually called in useEffect
useEffect(() => {
  if (user) {
    hydrateFromAuth0(user);
  }
}, [user]);

addOrder

Adds a completed order to the order history.
order
object
required
Order object containing order details, items, total, and timestamp
const addOrder = useUserStore((state) => state.addOrder);

addOrder({
  id: Date.now(),
  items: cart,
  total: 299.99,
  date: new Date().toISOString(),
  shipping: { /* shipping info */ },
  payment: { /* payment info */ }
});

Usage Examples

Checkout Shipping Form

import { useUserStore } from "../../store/useUserStore";
import { useAuth0 } from "@auth0/auth0-react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

export const CheckoutShipping = () => {
  const navigate = useNavigate();
  const { user } = useAuth0();

  const shipping = useUserStore((state) => state.shipping);
  const setShipping = useUserStore((state) => state.setShipping);
  const hydrateFromAuth0 = useUserStore((state) => state.hydrateFromAuth0);

  // Hydrate user data from Auth0
  useEffect(() => {
    if (user) {
      hydrateFromAuth0(user);
    }
  }, [user]);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setShipping({ [name]: value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    navigate("/checkout/payment");
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="fullName"
        placeholder="Full name"
        value={shipping.fullName}
        onChange={handleChange}
        required
      />
      <input
        name="address"
        placeholder="Address"
        value={shipping.address}
        onChange={handleChange}
        required
      />
      <input
        name="city"
        placeholder="City"
        value={shipping.city}
        onChange={handleChange}
        required
      />
      <button type="submit">Continue to Payment</button>
    </form>
  );
};

Account Management

import { useUserStore } from "../../store/useUserStore";
import { useState } from "react";

export const AccountPage = () => {
  const shipping = useUserStore((state) => state.shipping);
  const setShipping = useUserStore((state) => state.setShipping);
  const payment = useUserStore((state) => state.payment);
  const setPayment = useUserStore((state) => state.setPayment);

  const [editMode, setEditMode] = useState(false);

  const handleShippingChange = (e) => {
    const { name, value } = e.target;
    setShipping({ [name]: value });
  };

  const handlePaymentChange = (e) => {
    const { name, value } = e.target;
    setPayment({ [name]: value });
  };

  return (
    <div>
      <h2>Account Information</h2>
      
      <section>
        <h3>Shipping Address</h3>
        <input
          name="fullName"
          value={shipping.fullName}
          onChange={handleShippingChange}
          disabled={!editMode}
        />
        <input
          name="address"
          value={shipping.address}
          onChange={handleShippingChange}
          disabled={!editMode}
        />
        <input
          name="city"
          value={shipping.city}
          onChange={handleShippingChange}
          disabled={!editMode}
        />
      </section>

      <section>
        <h3>Payment Method</h3>
        <input
          name="cardNumber"
          value={payment.cardNumber}
          onChange={handlePaymentChange}
          disabled={!editMode}
        />
      </section>

      <button onClick={() => setEditMode(!editMode)}>
        {editMode ? "Save" : "Edit"}
      </button>
    </div>
  );
};

Order History

import { useUserStore } from "../../store/useUserStore";

export const OrderHistory = () => {
  const orders = useUserStore((state) => state.orders);

  if (orders.length === 0) {
    return <p>No orders yet</p>;
  }

  return (
    <div>
      <h2>Order History</h2>
      {orders.map((order) => (
        <div key={order.id}>
          <h3>Order #{order.id}</h3>
          <p>Date: {new Date(order.date).toLocaleDateString()}</p>
          <p>Total: ${order.total.toFixed(2)}</p>
          <ul>
            {order.items.map((item) => (
              <li key={item.id}>
                {item.title} x {item.qty} - ${(item.price * item.qty).toFixed(2)}
              </li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
};

Local Storage

The user store is persisted to local storage with the key user-checkout-storage. This allows shipping, payment, and order data to persist across sessions.

Storage Structure

{
  "state": {
    "shipping": {
      "fullName": "John Doe",
      "address": "123 Main St",
      "city": "San Francisco"
    },
    "payment": {
      "cardNumber": "4111111111111111",
      "expiryDate": "12/25",
      "cvc": "123"
    },
    "orders": [
      {
        "id": 1678901234567,
        "items": [...],
        "total": 299.99,
        "date": "2025-03-09T10:30:00.000Z"
      }
    ]
  },
  "version": 0
}

Clearing User Data

When a user logs out, it’s important to clear the persisted user data from local storage to ensure privacy and prevent data leakage between accounts.
const logout = () => {
  localStorage.removeItem("user-checkout-storage");
  auth0Logout({ logoutParams: { returnTo: window.location.origin } });
};

Integration with Auth0

The store integrates with Auth0 through the hydrateFromAuth0 method:
import { useAuth0 } from "@auth0/auth0-react";
import { useUserStore } from "../../store/useUserStore";
import { useEffect } from "react";

function MyComponent() {
  const { user } = useAuth0();
  const hydrateFromAuth0 = useUserStore((state) => state.hydrateFromAuth0);

  useEffect(() => {
    if (user) {
      hydrateFromAuth0(user);
    }
  }, [user, hydrateFromAuth0]);

  // Component implementation
}

Best Practices

  1. Partial Updates: Both setShipping and setPayment support partial updates
    // Only update the city
    setShipping({ city: "New York" });
    
    // Only update card number
    setPayment({ cardNumber: "4111111111111111" });
    
  2. Selector Optimization: Use specific selectors to prevent unnecessary re-renders
    // Good - only re-renders when shipping changes
    const shipping = useUserStore((state) => state.shipping);
    
    // Good - only re-renders when specific field changes
    const fullName = useUserStore((state) => state.shipping.fullName);
    
  3. Clear Sensitive Data: Always clear payment and user data on logout
    localStorage.removeItem("user-checkout-storage");
    
  4. Validate Before Storing: Validate payment data before persisting
    const handleSubmit = (e) => {
      e.preventDefault();
      if (validatePaymentData(payment)) {
        setPayment(payment);
        // Proceed with checkout
      }
    };
    

Build docs developers (and LLMs) love