seitu
Version:
173 lines (118 loc) • 4.46 kB
Markdown
# Solid (seitu/solid)
## useSubscription
Core Solid primitive from `seitu/solid`. Returns a Solid `Accessor<T>` (`() => T`) that stays in sync with the source. Works with any `Subscribable<T> & Readable<T>`. Accepts a raw instance or a reactive getter.
### Basic usage (module-level instance)
```tsx
import { useSubscription } from 'seitu/solid'
import { createStore } from 'seitu'
const count = createStore(0)
function Counter() {
const value = useSubscription(count)
return <button onClick={() => count.set(v => v + 1)}>{value()}</button>
}
```
### Inline subscription (getter form)
```tsx
import { useSubscription } from 'seitu/solid'
import { createScrollState } from 'seitu/web'
function ScrollTracker() {
let ref: HTMLDivElement | undefined
const state = useSubscription(() =>
createScrollState({ element: () => ref, direction: 'vertical' })
)
return <div ref={ref}>{state().top.reached ? 'at top' : 'scrolled'}</div>
}
```
### Reactive source (re-subscribes when a signal changes)
```tsx
import { createSignal } from 'solid-js'
import { useSubscription } from 'seitu/solid'
import { createWebStorageValue } from 'seitu/web'
function UserStorage(props: { userId: string }) {
const data = useSubscription(() =>
createWebStorageValue({ type: 'localStorage', key: `user:${props.userId}`, schema, defaultValue })
)
return <div>{data().name}</div>
}
```
### With selector (granular updates)
```tsx
const count = useSubscription(storage, { selector: v => v.count })
// count() only changes when the selected value changes
```
### Options
| Option | Type | Description |
|--------|------|-------------|
| `selector?` | `(value) => R` | Derive subset; the accessor updates only when it changes |
| `isEqual?` | `(prev, next) => boolean` | Custom equality (default: `deepEqual`) |
### Returns
`Accessor<R>` — a getter function. Read it with `value()` inside JSX or any tracked scope.
## Subscription (render-prop component)
Declarative render-prop component from `seitu/solid`. The children function runs once and
receives a Solid `Accessor<R>` — read it with `value()` so updates stay fine-grained.
```tsx
import { Subscription } from 'seitu/solid'
<Subscription value={storage}>
{value => <div>{value().count}</div>}
</Subscription>
<Subscription value={storage} selector={v => v.count}>
{count => <div>{count()}</div>}
</Subscription>
```
### Props
| Prop | Type | Description |
|------|------|-------------|
| `value` | `Subscribable & Readable` | The reactive source |
| `selector?` | `(value) => R` | Optional selector for granular updates |
| `children` | `(value: Accessor<R>) => JSX.Element` | Render function receiving an accessor |
## Common Mistakes
### [CRITICAL] Reading the accessor without calling it
Wrong:
```tsx
const value = useSubscription(store)
return <div>{value}</div>
```
Correct:
```tsx
const value = useSubscription(store)
return <div>{value()}</div>
```
`useSubscription` returns a Solid `Accessor`; you must call it (`value()`) to read and track the value. The same applies to the `Subscription` component's children argument — call it (`value()`) inside JSX, don't treat it as a plain value.
### [HIGH] Passing reactive props directly instead of a getter
Wrong:
```tsx
function Item(props: { store: Store<number> }) {
const value = useSubscription(props.store)
}
```
Correct:
```tsx
function Item(props: { store: Store<number> }) {
const value = useSubscription(() => props.store)
}
```
Reading `props.store` outside a tracked scope loses reactivity; the getter form re-subscribes when the prop changes.
### [CRITICAL] Importing from seitu/react in Solid
Wrong:
```ts
import { useSubscription } from 'seitu/react'
```
Correct:
```ts
import { useSubscription } from 'seitu/solid'
```
Solid apps must use the `seitu/solid` primitive/component — the React versions rely on `useSyncExternalStore` and React internals.
### [LOW] Preferring the component over the primitive without reason
Wrong:
```tsx
<Subscription value={s}>{v => <Child value={v()} />}</Subscription>
```
Correct:
```tsx
const v = useSubscription(s); return <Child value={v()} />
```
`useSubscription` is simpler for most cases; `Subscription` is for render-prop composition.
## See also
- [`create-scroll-state`](create-scroll-state.md) — Scroll tracking uses the getter + ref pattern in Solid.
## Source
`src/solid/hooks/index.ts`, `src/solid/components/index.ts`