Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Context

Context is a way to hand data to all nested components at once, without threading it through props at every level. Context usually carries things needed in many places: the task list with its update functions, the current user, the color theme.

Creating a context

Create the file src/context/TodoContext.jsx. It will hold the context itself, a provider component and a function for reading:

// src/context/TodoContext.jsx
import { createContext, useContext } from 'solid-js';
import { createStore } from 'solid-js/store';
 
const TodoContext = createContext();
 
export function TodoProvider(props) {
  const [todos, setTodos] = createStore([]);
 
  const add = (title) => setTodos(todos.length, { id: Date.now(), title, done: false });
  const toggle = (id) => setTodos((t) => t.id === id, 'done', (d) => !d);
 
  return (
    <TodoContext.Provider value={{ todos, add, toggle }}>
      {props.children}
    </TodoContext.Provider>
  );
}
 
export function useTodos() {
  return useContext(TodoContext);
}

TodoProvider creates the store and puts it, together with the functions, into the value attribute. Everything inside the provider can get that value.

Adding the provider

Wrap the app in TodoProvider:

// src/App.jsx
import { TodoProvider } from './context/TodoContext';
import AddTodo from './components/AddTodo';
import TodoList from './components/TodoList';
 
export default function App() {
  return (
    <TodoProvider>
      <h1>My tasks</h1>
      <AddTodo />
      <TodoList />
    </TodoProvider>
  );
}

Reading the context

Components inside get the data by calling useTodos():

// src/components/TodoList.jsx
import { For } from 'solid-js';
import { useTodos } from '../context/TodoContext';
 
export default function TodoList() {
  const { todos, toggle } = useTodos();
 
  return (
    <ul>
      <For each={todos}>
        {(todo) => <li onClick={() => toggle(todo.id)}>{todo.title}</li>}
      </For>
    </ul>
  );
}
// src/components/AddTodo.jsx
import { useTodos } from '../context/TodoContext';
 
export default function AddTodo() {
  const { add } = useTodos();
 
  return <button onClick={() => add('New task')}>Add</button>;
}

A click on the button in AddTodo adds a task, and it appears in TodoList right away, although the two components are not linked by props. Destructuring the context value is fine here: todos is a store and stays reactive.

What goes where

PartPurpose
createContext()Creates the context
Context.ProviderSets the value for nested components
useContext(Context)Reads the value

Common mistake

Calling useTodos() in a component that is outside TodoProvider, for example right in App. The function returns undefined, and accessing todos throws an error. The provider must sit higher in the tree than every component that reads the context.

Thanks for reading!

Donate to the site — we work hard for you.