createProvider

Create isolated, component-scoped stores with React Context integration

When to Use Providers

Perfect for state that should be isolated per component instance:

  • Modal or dialog state
  • Form state that shouldn't persist
  • Tab or accordion panel state
  • Feature-specific state that shouldn't be global

API Reference

Syntax

const [Provider, useFindStore, storeActions] = createProvider(initialState);

Parameters

  • initialState: Object defining the store's initial state and actions

Returns

A tuple containing:

  • Provider: React component that provides isolated store instance to children
  • useFindStore: Hook to access the store instance within provider tree
  • storeActions: Utility object with getStore, getState, setState methods

Examples

Simple Isolated State

import { createProvider, useStoreValue } from "zenbox";

// Create isolated session state per provider
const [SessionProvider, useFindSessionStore] = createProvider({
  user: null,
  isAuthenticated: false,
  sessionId: null,
  preferences: {
    theme: "light",
    language: "en",
  },
});

function UserProfile() {
  const store = useFindSessionStore();
  const { user, isAuthenticated } = useStoreValue(store);

  if (!isAuthenticated) {
    return <div>Please log in</div>;
  }

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <p>Session: {store.value.sessionId}</p>
    </div>
  );
}

function App() {
  return (
    <SessionProvider initialState={{ sessionId: "abc123" }}>
      <UserProfile />
    </SessionProvider>
  );
}

Provider with Actions

const [GameProvider, useFindGameStore, gameActions] = createProvider({
  // State
  score: 0,
  level: 1,
  lives: 3,
  gameState: "playing" as "playing" | "paused" | "gameOver",

  // Actions
  increaseScore(points) {
    gameActions.setState((state) => {
      state.score += points;
      // Auto level-up every 1000 points
      if (state.score >= state.level * 1000) {
        state.level += 1;
      }
    });
  },

  loseLife() {
    gameActions.setState((state) => {
      state.lives -= 1;
      if (state.lives <= 0) {
        state.gameState = "gameOver";
      }
    });
  },

  resetGame() {
    gameActions.setState((state) => {
      state.score = 0;
      state.level = 1;
      state.lives = 3;
      state.gameState = "playing";
    });
  },
});

function GameComponent() {
  const store = useFindGameStore();
  const { score, level, lives, gameState } = useStoreValue(store);

  return (
    <div className="game">
      <div className="hud">
        <span>Score: {score}</span>
        <span>Level: {level}</span>
        <span>Lives: {lives}</span>
      </div>

      {gameState === "gameOver" ? (
        <div className="game-over">
          <h2>Game Over! Final Score: {score}</h2>
          <button onClick={store.value.resetGame}>Play Again</button>
        </div>
      ) : (
        <div className="game-area">
          <button onClick={() => store.value.increaseScore(100)}>
            +100 Points
          </button>
          <button onClick={store.value.loseLife}>Lose Life</button>
        </div>
      )}
    </div>
  );
}

// Multiple independent game instances
function App() {
  return (
    <div className="app">
      <h1>Game Arena</h1>

      <div className="games">
        <GameProvider>
          <h2>Player 1</h2>
          <GameComponent />
        </GameProvider>

        <GameProvider>
          <h2>Player 2</h2>
          <GameComponent />
        </GameProvider>
      </div>
    </div>
  );
}

Nested Providers

function App() {
  return (
    <SessionProvider>
      <GameProvider>
        <GameComponent />
      </GameProvider>
    </SessionProvider>
  );
}

Provider vs Global Store

FeaturecreateProvidercreateStore
ScopeComponent treeGlobal application
Isolation✅ Perfect isolation❌ Shared across app
Context✅ React Context❌ Direct access
Multiple instances✅ Independent❌ Single instance
Best forModals, forms, widgetsApp state, user data

Best Practices

Use for truly isolated state

// Good: Modal state shouldn't leak between instances
const [ModalProvider, useModal] = createProvider({
  isOpen: false,
  step: 1,
});

Initialize with meaningful defaults

<GameProvider initialState={{
  difficulty: "hard",
  playerName: "Anonymous"
}}>

Keep providers focused

// Good: Single responsibility
const [FormProvider, useForm] = createProvider({
  values: {},
  errors: {},
  isSubmitting: false,
});

Don't use for global state

// Bad: User auth should be global, not per-provider
const [AuthProvider] = createProvider({
  currentUser: null, // This should be global!
});