Getting Started
Recommended Usage

Recommended Approach

For production applications, we recommend using factory functions to create your state. This approach provides better organization, type safety, and reusability compared to inline useReState calls.

Why Factory Functions?

BenefitDescription
EncapsulationState definition lives in one place
Type SafetyTypes are defined once, not repeated
ReusabilityExport named hooks and actions
TestabilityEasier to mock and test state
ScalabilityClean organization as your app grows

Option 1: createReStateMethods (All-in-One)

The simplest way to get a complete state management solution. One call gives you everything:

// states/cart.ts
import { createReStateMethods } from '@raulpesilva/re-state';
 
interface CartItem {
  id: string;
  name: string;
  price: number;
  quantity: number;
}
 
interface CartState {
  items: CartItem[];
  total: number;
}
 
const initialCart: CartState = {
  items: [],
  total: 0,
};
 
export const {
  useCart,        // Hook: [value, setValue]
  useCartSelect,  // Hook: value only
  dispatchCart,   // Update from anywhere
  getCart,        // Read without subscribing
  resetCart,      // Reset to initial
} = createReStateMethods('cart', initialCart);
 
// Custom actions
export const addItem = (item: Omit<CartItem, 'quantity'>) => {
  dispatchCart(prev => {
    const existing = prev.items.find(i => i.id === item.id);
    const items = existing
      ? prev.items.map(i => i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i)
      : [...prev.items, { ...item, quantity: 1 }];
    const total = items.reduce((sum, i) => sum + i.price * i.quantity, 0);
    return { items, total };
  });
};
 
export const removeItem = (id: string) => {
  dispatchCart(prev => {
    const items = prev.items.filter(i => i.id !== id);
    const total = items.reduce((sum, i) => sum + i.price * i.quantity, 0);
    return { items, total };
  });
};
 
export const clearCart = () => resetCart();
// components/Cart.tsx
import { useCartSelect, addItem, removeItem, clearCart } from '../states/cart';
 
function Cart() {
  const cart = useCartSelect();
 
  return (
    <div>
      <h2>Cart ({cart.items.length} items)</h2>
      {cart.items.map(item => (
        <div key={item.id}>
          <span>{item.name} x{item.quantity}</span>
          <button onClick={() => removeItem(item.id)}>Remove</button>
        </div>
      ))}
      <p>Total: ${cart.total.toFixed(2)}</p>
      <button onClick={clearCart}>Clear Cart</button>
    </div>
  );
}

Best for: Most use cases. Start here unless you have a specific reason not to.


Option 2: Individual Factory Functions

For more control, use individual factory functions. This is useful when you only need specific utilities or want to customize naming.

// states/theme.ts
import {
  createReState,
  createReStateSelect,
  createReStateDispatch,
  createGetReState,
} from '@raulpesilva/re-state';
 
type Theme = 'light' | 'dark' | 'system';
 
const THEME_KEY = 'theme';
const DEFAULT_THEME: Theme = 'system';
 
// Create only what you need
export const useTheme = createReState<Theme>(THEME_KEY, DEFAULT_THEME);
export const useThemeValue = createReStateSelect<Theme>(THEME_KEY);
export const setTheme = createReStateDispatch<Theme>(THEME_KEY);
export const getTheme = createGetReState<Theme>(THEME_KEY);
 
// Custom actions
export const toggleTheme = () => {
  setTheme(current => current === 'light' ? 'dark' : 'light');
};
 
export const resetTheme = () => setTheme(DEFAULT_THEME);

Best for: When you want custom method names or only need specific utilities.


Organizing State Files

Small Projects

Keep all states in a single states/ folder:

src/
  states/
    user.ts
    cart.ts
    theme.ts
    index.ts    # Re-export everything
  components/
  ...
// states/index.ts
export * from './user';
export * from './cart';
export * from './theme';

Large Projects

Group related states together:

src/
  features/
    auth/
      states/
        user.ts
        session.ts
      components/
      hooks/
    cart/
      states/
        cart.ts
        wishlist.ts
      components/
    settings/
      states/
        theme.ts
        preferences.ts
      components/

Patterns and Best Practices

1. Co-locate Actions with State

Keep actions in the same file as the state they modify:

// states/counter.ts
export const { useCounter, dispatchCounter, resetCounter } = 
  createReStateMethods('counter', 0);
 
// Actions live here, not in components
export const increment = () => dispatchCounter(prev => prev + 1);
export const decrement = () => dispatchCounter(prev => prev - 1);
export const incrementBy = (amount: number) => dispatchCounter(prev => prev + amount);

2. Use Select Hook for Read-Only Components

When a component only displays state, use the select hook:

// This component only reads, never writes
function CounterBadge() {
  const count = useCounterSelect(); // Not useCounter()
  return <span className="badge">{count}</span>;
}

3. Use Dispatch for Side Effects

Keep side effects in actions, not components:

// states/user.ts
export const login = async (credentials: Credentials) => {
  dispatchLoading(true);
  try {
    const response = await api.login(credentials);
    dispatchUser(response.user);
    dispatchToken(response.token);
    localStorage.setItem('token', response.token);
  } catch (error) {
    dispatchError(error.message);
  } finally {
    dispatchLoading(false);
  }
};
// components/LoginForm.tsx
import { login } from '../states/user';
 
function LoginForm() {
  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    login({ email, password }); // Clean component, logic in state
  };
  // ...
}

4. Subscribe to Changes Outside React

Use onReStateChange for side effects that don't belong in components:

// states/theme.ts
import { createReStateMethods, onReStateChange } from '@raulpesilva/re-state';
 
export const { useTheme, dispatchTheme, getTheme } = 
  createReStateMethods('theme', 'light');
 
// Sync with DOM
onReStateChange(() => {
  document.documentElement.setAttribute('data-theme', getTheme());
}, ['theme']);
 
// Persist to localStorage
onReStateChange(() => {
  localStorage.setItem('theme', getTheme());
}, ['theme']);

Quick Reference

NeedUse
Complete state with all utilitiescreateReStateMethods
Hook with read + writecreateReState
Hook with read onlycreateReStateSelect
Update from outside ReactcreateReStateDispatch
Read without subscribingcreateGetReState
React to changes outside ReactonReStateChange
Reset all statesresetReState

Next Steps