Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/solid-2.0/04-stores.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ setStore(s => {

This pairs naturally with `createProjection`, where returning a value from the derive function uses reconciliation automatically (keyed by `options.key`, default `"id"`).

Don't keep mutating an object after handing it to a store. A later in-place change to that object isn't observed, and assigning or reconciling the same object again doesn't pick it up either, because the store sees the same reference. Pass a fresh object instead (a new response, or a copy such as `{ ...next }`). This mostly affects test fixtures and caches that reuse one response object.

### Shallow stores (`shallow: true`)

By default stores track at property level all the way down — that is the model, and it is the right choice for almost all state. `shallow: true` is a **specialized performance opt-in** for one identifiable workload shape, not a general recommendation: reach for it when profiling shows ingestion cost on data whose records change wholesale.
Expand Down
2 changes: 2 additions & 0 deletions documentation/solid-2.0/07-dom.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ let el;
<div ref={[node => (el = node), autofocus]} />; // assigned
```

A ref runs before the element is inserted into the page. At that point `el.isConnected` is `false`, and `el.ownerDocument` is the inert document of the template the element was cloned from, so `el.ownerDocument.defaultView` is `null`. Don't reach the window or document through the element inside a ref: use the global `window`/`document`, or do work that needs the attached element (measuring, reading computed styles) in `onSettled`.

#### Two-phase directive factories (owned setup, unowned application)

The recommended directive pattern is **two-phase**, similar in spirit to “split effects” (compute phase vs apply phase):
Expand Down
2 changes: 2 additions & 0 deletions documentation/solid-2.0/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,8 @@ const value = createMemo(async function* () {
});
```

Cleanup is not immediate. When the owner is disposed, or a re-run replaces the stream, Solid calls the generator's `return()`, but a generator waiting at an `await` can't finish until that `await` settles. The source stays subscribed until it produces another value or ends. If the memo only forwards values, return the iterable itself (`createMemo(() => observable$)`): Solid closes it right away. A source that isn't an async iterable (a callback subscription, an Observable) needs an adapter (see #4182, which also tracks prompt cleanup for generators).

**Solid → External (`observable`):** There's no drop-in replacement. `observable()` produced a standard Observable that external libraries could subscribe to. In 2.0, use `createEffect` to push signal changes to an external subscriber:

```js
Expand Down
8 changes: 8 additions & 0 deletions packages/solid/CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
createOptimisticStore,
isPending,
latest,
createDeferred,
refresh,
affects,
untrack,
Expand Down Expand Up @@ -225,6 +226,11 @@ const user = createMemo(() => fetchUser(id()));
// Peek at the in-flight value during a transition
latest(id);

// A memo that may lag but never leads (React's useDeferredValue): after its
// first answer, a refetch keeps the last value on screen instead of holding
// the change; isPending(() => rows()) reports the refetch.
const rows = createDeferred(() => fetchRows(period()));

// Force recompute of a derived read after a server write.
// Action: call from handlers/effects/actions, not pure reads.
// A bare refresh() re-asks the SAME question — it is quiet (isPending stays
Expand Down Expand Up @@ -441,6 +447,8 @@ hydrate(() => <App />, document.getElementById("root")!);
<button ref={[autofocus, tooltip({ content: "Save" })]} />
```

A ref runs before the element is attached: `el.isConnected` is `false` and `el.ownerDocument.defaultView` is `null`. Use the global `window`/`document`, or do layout-dependent work in `onSettled`.

Two-phase directive (recommended):

```ts
Expand Down
3 changes: 2 additions & 1 deletion packages/solid/skills/reactivity-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ return a cleanup function from the callback instead.
### SETTLED_CLEANUP_UNOWNED

`onSettled` returned a cleanup while running in an unowned scope, so the
cleanup cannot be honored. Call the setup helper from an owned scope (the
cleanup cannot be honored. Throws in dev; in production the cleanup is
dropped. Call the setup helper from an owned scope (the
component body), not from an event handler, tracked effect, or another
`onSettled`.

Expand Down
Loading