Context

Context provides a way to share state between components without prop drilling.

Creating a Context

import { createContext } from '@emberkit/core';

interface ThemeContext {
  theme: 'light' | 'dark';
  toggle: () => void;
}

const ThemeContext = createContext<ThemeContext>({
  theme: 'light',
  toggle: () => {},
});

Providing Values

Use the Provider to make a value available to descendant components:

import { createContext, createSignal } from '@emberkit/core';

const ThemeContext = createContext<{ theme: string; toggle: () => void }>();

function App() {
  const [theme, setTheme] = createSignal('light');
  const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');

  return (
    <ThemeContext.Provider value={{ theme: theme(), toggle }}>
      <Header />
      <main>...</main>
    </ThemeContext.Provider>
  );
}

Consuming Values

Use useContext to read context in any descendant component:

import { useContext } from '@emberkit/core';

function Header() {
  const ctx = useContext(ThemeContext);
  return (
    <header>
      <span>Current theme: {ctx.theme}</span>
      <button onClick={ctx.toggle}>Toggle</button>
    </header>
  );
}

Using the use Method

Each context returned by createContext also has a use shorthand:

const ThemeContext = createContext<{ theme: string }>({ theme: 'light' });

function Header() {
  const { theme } = ThemeContext.use();
  return <span>Theme: {theme}</span>;
}

Default Values

If no Provider is above a consumer, the default value is used:

const UserContext = createContext<{ name: string }>({ name: 'Guest' });

// In a component without a Provider above:
function Greeting() {
  const user = useContext(UserContext);
  return <span>Hello, {user.name}</span>; // "Hello, Guest"
}

Complete Example

import { createContext, useContext, createSignal } from '@emberkit/core';

interface AuthContext {
  user: string | null;
  login: (name: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContext>({
  user: null,
  login: () => {},
  logout: () => {},
});

function AuthProvider({ children }: { children: unknown }) {
  const [user, setUser] = createSignal<string | null>(null);

  return (
    <AuthContext.Provider value={{
      user: user(),
      login: (name) => setUser(name),
      logout: () => setUser(null),
    }}>
      {children}
    </AuthContext.Provider>
  );
}

function Navbar() {
  const { user, logout } = useContext(AuthContext);

  return (
    <nav>
      {user ? (
        <>
          <span>Welcome, {user}</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <span>Please log in</span>
      )}
    </nav>
  );
}

Next Steps