[]previous and next page

Context, props and children

createContext and useContext keep their names.

For agentsUpdated

createContext and useContext keep their names. The provider can be written either way, and a context without a provider and without a default throws, which makes Solid's "guard useMyContext" helper unnecessary.

Context, selector and list: Solid and ShadowJS#

Solid (not executed):

tsx
import { createContext, createSelector, createSignal, For, useContext } from "solid-js";

const Theme = createContext(() => "light");

function Row(props) {
  const theme = useContext(Theme);
  return <li classList={{ active: props.active, dark: theme() === "dark" }}>{props.label}</li>;
}

function Picker() {
  const [theme, setTheme] = createSignal("light");
  const [selected, setSelected] = createSignal(1);
  const isSelected = createSelector(selected);
  return (
    <Theme.Provider value={theme}>
      <For each={items}>{(item) => <Row active={isSelected(item.id)} label={item.label} />}</For>
    </Theme.Provider>
  );
}

ShadowJS: useSelector, useStore, and either provider form. Selecting a row updates only the two rows whose state changed, and a theme change reaches every row through the context accessor.

src/picker.tsxtsx
import { type Accessor, createContext, useContext, useEffect, useSelector, useStore } from "@shadow-js/core";
import { For } from "@shadow-js/web";

const Theme = createContext<Accessor<string>>(() => "light");

/** Every time a row's `active` binding ran, as `id:active`. */
export const log: string[] = [];

const ITEMS = [
  { id: 1, label: "One" },
  { id: 2, label: "Two" },
  { id: 3, label: "Three" },
];

function Row(props: { id: number; label: string; active: boolean; onSelect: (id: number) => void }) {
  const theme = useContext(Theme);
  useEffect(() => {
    log.push(`${props.id}:${props.active}`);
  });
  return (
    <li data-testid={`row-${props.id}`} classList={{ active: props.active, dark: theme() === "dark" }}>
      <button type="button" onClick={() => props.onSelect(props.id)}>
        {props.label}
      </button>
    </li>
  );
}

export function Picker() {
  const [theme, setTheme] = useStore("light");
  const [selected, setSelected] = useStore(1);
  const isSelected = useSelector(selected);
  return (
    <Theme value={theme}>
      <ul>
        <For each={ITEMS}>
          {(item) => <Row id={item.id} label={item.label} active={isSelected(item.id)} onSelect={setSelected} />}
        </For>
      </ul>
      <button type="button" onClick={() => setTheme("dark")}>
        Dark
      </button>
    </Theme>
  );
}