Observer pattern
For a component to respond to signal changes, it must be wrapped with observer. This is the most important pattern in StartupJS.
What observer does
observer is a function that wraps your component and makes it reactive. When any signal read during render changes, the component re-renders automatically. You do not need to manage subscriptions or call setState -- it all happens behind the scenes.
observer also wraps your component in a Suspense boundary. This means that while data is being fetched (via useSub), a loading state is shown automatically. You do not need to handle loading states yourself.
Example
This component:
- Subscribes to a user document with
useSub($.users[userId]). - Reads the user's name with
$user.name.get(). - Updates the name with
$user.name.set(value)when the input changes. - Re-renders automatically whenever
namechanges -- whether from this client or from another.
When to use observer
Wrap a component with observer whenever it:
- Reads signal values with
.get() - Uses
useSubto subscribe to data - Uses local signals created with
$()
If a component only receives plain props (strings, numbers, etc.) and does not read any signals, you do not need observer. But in practice, most components in a StartupJS app use it.
How it works
Behind the scenes, observer tracks which signals your component reads during render. It creates a dependency list automatically. When any of those signals change, only the components that depend on them re-render -- not the entire tree.
This is more efficient than React's default behavior, where a parent re-render causes all children to re-render too.
For a general explanation of the Observer design pattern, see Refactoring Guru.