From 680d33f7363f2dbf345dd77ecd3d7f6f784e91ed Mon Sep 17 00:00:00 2001 From: Sarah Gerrard <98355961+LadyBluenotes@users.noreply.github.com> Date: Tue, 18 Nov 2025 19:26:03 +0000 Subject: [PATCH] wip --- .../basic-reactivity/create-signal.mdx | 179 ++++++++---------- 1 file changed, 78 insertions(+), 101 deletions(-) diff --git a/src/routes/reference/basic-reactivity/create-signal.mdx b/src/routes/reference/basic-reactivity/create-signal.mdx index f6de896ac9..26c51b46ca 100644 --- a/src/routes/reference/basic-reactivity/create-signal.mdx +++ b/src/routes/reference/basic-reactivity/create-signal.mdx @@ -15,147 +15,124 @@ description: >- values that change over time and automatically update your UI when they do. --- -Signals are the most basic reactive primitive. -They track a single value (which can be a value of any type) that changes over time. +`createSignal` creates Solid's core reactive primitive: a stateful value with an accessor (getter) and a setter. +The accessor tracks every computation that reads it, such as effects, memos, or JSX, so those observers will rerun when the stored value changes. + +`createSignal` updates run synchronously, making the new value available within the same tick and scheduling dependents immediately. +Before notifying observers, Solid runs an equality comparator (strict equality by default) so unchanged values do not trigger unnecessary work. +This can be overridden by the [`equals`](#equals) option to disable comparisons or supply custom logic for mutable data. + +## Import ```tsx import { createSignal } from "solid-js" +``` -function createSignal( - initialValue: T, - options?: { - equals?: false | ((prev: T, next: T) => boolean) - name?: string - internal?: boolean - } -): [get: () => T, set: (v: T) => T] - -// available types for return value of createSignal: -import type { Signal, Accessor, Setter } from "solid-js" -type Signal = [get: Accessor, set: Setter] -type Accessor = () => T -type Setter = (v: T | ((prev?: T) => T)) => T +## Type +```tsx +function createSignal(): Signal +function createSignal(value: T, options?: SignalOptions): Signal ``` -The Signal's value starts out equal to the passed first argument `initialValue` (or undefined if there are no arguments). -The `createSignal` function returns a pair of functions as a two-element array: a getter (or accessor) and a setter. -In typical use, you would destructure this array into a named Signal like so: +`Signal` is a tuple that contains an accessor and a setter: `[Accessor, Setter]`. +The setter accepts either a value or a function `(prev: T) => T` and returns the new value. + +### Helper types + +These aliases mirror the tuple returned by `createSignal` and are useful when annotating variables or function signatures in TypeScript-heavy projects. ```tsx -const [count, setCount] = createSignal(0) -const [ready, setReady] = createSignal(false) +import type { Accessor, Setter, Signal } from "solid-js" + +type Signal = [Accessor, Setter] +type Accessor = () => T +type Setter = (value: U | ((prev: T) => U)) => U ``` -Calling the getter (e.g., `count()` or `ready()`) returns the current value of the Signal. +## Parameters -Crucial to automatic dependency tracking, calling the getter within a tracking scope causes the calling function to depend on this Signal, so that function will rerun if the Signal gets updated. +### `initialValue` -Calling the setter (e.g., `setCount(nextCount)` or `setReady(nextReady)`) sets the Signal's value and updates the Signal (triggering dependents to rerun) if the value actually changed (see details below). -The setter takes either the new value for the signal or a function that maps the previous value of the signal to a new value as its only argument. -The updated value is also returned by the setter. As an example: +- **Type:** `T` +- **Required:** No -```tsx -// read signal's current value, and -// depend on signal if in a tracking scope -// (but nonreactive outside of a tracking scope): -const currentCount = count() +The initial value stored by the signal. +When omitted, the accessor initially returns `undefined`. -// or wrap any computation with a function, -// and this function can be used in a tracking scope: -const doubledCount = () => 2 * count() +### `options` -// or build a tracking scope and depend on signal: -const countDisplay =
{count()}
+- **Type:** `SignalOptions` +- **Required:** No -// write signal by providing a value: -setReady(true) +An optional configuration object. +It supports the following properties: -// write signal by providing a function setter: -const newCount = setCount((prev) => prev + 1) +#### `equals` -``` +- **Default:** strict equality (`===`). +- **Required:** No. -:::note - If you want to store a function in a Signal you must use the function form: +- Provide `false` to emit updates on every setter call, even if the value is the same reference. +- Provide a comparator function `(prev, next) => boolean` to decide when the stored value should be considered unchanged (`true`) or changed (`false`). +- In-place mutation of objects is ignored unless you disable equality checking with `equals: false` or provide a comparator that detects the change. - ```tsx - setValue(() => myFunction); - ``` +#### `name` - However, functions are not treated specially as the `initialValue` argument to `createSignal`, so you can pass a - function initial value as is: +- **Default:** `undefined`. - ```tsx - const [func, setFunc] = createSignal(myFunction); - ``` +- Adds a descriptive label that surfaces in the [Solid Devtools](https://github.com/thetarnav/solid-devtools) inspector. +- This metadata is available only in development builds. -::: +#### `internal` -## Options +- **Default:** `false`. -| Name | Type | Default | Description | -| ---------- | ------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `equals` | `false \| ((prev: T, next: T) => boolean)` | `===` | A function that determines whether the Signal's value has changed. If the function returns true, the Signal's value will not be updated and dependents will not rerun. If the function returns false, the Signal's value will be updated and dependents will rerun. | -| `name` | `string` | | A name for the Signal. This is useful for debugging. | -| `internal` | `boolean` | `false` | If true, the Signal will not be accessible in the devtools. | +- Hides the signal from Solid Devtools while keeping all runtime behavior. +- This flag has an effect only in development builds. -### `equals` +## Return value -The `equals` option can be used to customize the equality check used to determine whether the Signal's value has changed. -By default, the equality check is a strict equality check (`===`). -If you want to use a different equality check, you can pass a custom function as the `equals` option. -The custom function will be called with the previous and next values of the Signal as arguments. -If the function returns true, the Signal's value will not be updated and dependents will not rerun. -If the function returns false, the Signal's value will be updated and dependents will rerun. +`createSignal` returns a tuple `[getter, setter]`. -```tsx -const [count, setCount] = createSignal(0, { - equals: (prev, next) => prev === next, -}) -``` +- The **getter** (`Accessor`) reads the current value and registers dependencies when used inside a tracking scope. +- The **setter** (`Setter`) updates the value. +- The setter accepts either the next value or a function of the previous value. +- The setter returns the updated value. -Here are some examples of this option in use: +## Examples -```tsx -// use { equals: false } to allow modifying object in-place; -// normally this wouldn't be seen as an update because the -// object has the same identity before and after change -const [object, setObject] = createSignal({ count: 0 }, { equals: false }) -setObject((current) => { - current.count += 1 - current.updated = new Date() - return current -}) +### Basic state -// use { equals: false } to create a signal that acts as a trigger without storing a value: -const [depend, rerun] = createSignal(undefined, { equals: false }) -// now calling depend() in a tracking scope -// makes that scope rerun whenever rerun() gets called +```tsx +const [count, setCount] = createSignal(0) -// define equality based on string length: -const [myString, setMyString] = createSignal("string", { - equals: (newVal, oldVal) => newVal.length === oldVal.length, +createEffect(() => { + console.log(`Count is ${count()}`) }) -setMyString("string") // considered equal to the last value and won't cause updates -setMyString("stranger") // considered different and will cause updates +setCount(1) ``` -### `name` - -The `name` option can be used to give the Signal a name. -This is useful for debugging. The name will be displayed in the devtools. +### Trigger without data ```tsx -const [count, setCount] = createSignal(0, { name: "count" }) -``` +const [trigger, rerun] = createSignal(undefined, { equals: false }) + +createEffect(() => { + trigger() + console.log("Effect reran") +}) -### `internal` +rerun() +``` -The `internal` option can be used to hide the Signal from the devtools. -This is useful for Signals that are used internally by a component and should not be exposed to the user. +### Toggle helper ```tsx -const [count, setCount] = createSignal(0, { internal: true }) -``` +const [isOpen, setOpen] = createSignal(false) + +function toggle() { + setOpen((prev) => !prev) +} +``` \ No newline at end of file