Skip to content

React migration: use useSignalValue for module-level signals in render #423

Description

@JonasJesus42

Problem

When porting components from Preact (Deco Fresh) to React (TanStack Start), module-level signals from @decocms/blocks/sdk/signal behave differently:

  • Preact: reading signal.value in render automatically subscribes the component and triggers re-renders on change (Preact signals magic).
  • React: reading signal.value in render does NOT subscribe the component. The value is read once and never updates — the component appears broken (e.g. drawers never open, modals stay closed).

Required pattern in React

// ❌ BROKEN in React — reads once, never re-renders on signal change
const { displayCart } = useUI();
return <Drawer open={displayCart.value} />;

// ✅ CORRECT — useSignalValue uses useSyncExternalStore under the hood
import { useSignalValue } from "~/sdk/signal";
const { displayCart } = useUI();
const displayCartValue = useSignalValue(displayCart);
return <Drawer open={displayCartValue} />;

Writing to signals (signal.value = x) is safe anywhere — only reading in render requires useSignalValue.

Notes

  • useSignal(v) (component-scoped) is backed by useState and is safe to read in render within the same component.
  • Only module-level signals (from signal() in useUI.ts or similar) need useSignalValue when read in render.
  • This pattern affects all UI state components: drawers, modals, toasts, search bars, etc.

Affected files in a typical TanStack Start migration

  • Any component that reads a shared signal from useUI() to control visibility (open/close state).
  • Common symptoms: drawer opens briefly and closes, modal never opens, CSS class never updates reactively.

Related

  • useSignalValue is available from ~/sdk/signal — re-exported from @decocms/blocks/sdk/signal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions