diff --git a/lunaria.config.json b/lunaria.config.json deleted file mode 100644 index 63fd45cd1..000000000 --- a/lunaria.config.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "$schema": "./node_modules/@lunariajs/core/config.schema.json", - "repository": { - "name": "solidjs/solid-docs-next", - "branch": "main", - "hosting": "github" - }, - "defaultLocale": { - "label": "English", - "lang": "en" - }, - "files": [ - { - "type": "universal", - "location": "src/routes/**/*.{mdx,md}", - "pattern": "src/routes/@lang/@path" - }, - { - "location": "src/i18n/dictionaries/**/ui.ts", - "pattern": "src/i18n/dictionaries/@lang/@path", - "type": "dictionary" - } - ], - "locales": [ - { - "label": "Português do Brasil", - "lang": "pt-br" - } - ], - "outDir": "./public/i18n-status", - "dashboard": { - "site": "https://docs.solidjs.com/i18n-status", - "title": "Solid Docs Translation Status", - "description": "Translation progress tracker for the Solid Docs site", - "basesToHide": ["src/routes/", "src/i18n/dictionaries/"], - "favicon": { - "external": [ - { - "link": "https://docs.solidjs.com/favicon.ico", - "type": "image/x-icon" - }, - { - "link": "https://docs.solidjs.com/favicon.svg", - "type": "image/svg+xml" - } - ] - }, - "customCss": ["./lunaria/styles.css"], - "ui": { - "statusByLocale.heading": "Translation progress by locale", - "statusByLocale.incompleteLocalizationLink": "incomplete translation", - "statusByLocale.outdatedLocalizationLink": "outdated translation", - "statusByLocale.completeLocalization": "This translation is complete, amazing job! 🎉", - "statusByFile.heading": "Translation status by file" - } - } -} diff --git a/lunaria/styles.css b/lunaria/styles.css deleted file mode 100644 index cd92464a8..000000000 --- a/lunaria/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -:root { - --tw-bg-opacity: 1; - background-color: rgb(15 23 42 / var(--tw-bg-opacity)); -} diff --git a/src/routes/advanced-concepts/data.json b/old pages/advanced-concepts/data.json similarity index 100% rename from src/routes/advanced-concepts/data.json rename to old pages/advanced-concepts/data.json diff --git a/src/routes/advanced-concepts/fine-grained-reactivity.mdx b/old pages/advanced-concepts/fine-grained-reactivity.mdx similarity index 100% rename from src/routes/advanced-concepts/fine-grained-reactivity.mdx rename to old pages/advanced-concepts/fine-grained-reactivity.mdx diff --git a/src/routes/concepts/components/basics.mdx b/old pages/concepts/components/basics.mdx similarity index 100% rename from src/routes/concepts/components/basics.mdx rename to old pages/concepts/components/basics.mdx diff --git a/src/routes/concepts/components/class-style.mdx b/old pages/concepts/components/class-style.mdx similarity index 100% rename from src/routes/concepts/components/class-style.mdx rename to old pages/concepts/components/class-style.mdx diff --git a/src/routes/concepts/components/data.json b/old pages/concepts/components/data.json similarity index 100% rename from src/routes/concepts/components/data.json rename to old pages/concepts/components/data.json diff --git a/src/routes/concepts/components/event-handlers.mdx b/old pages/concepts/components/event-handlers.mdx similarity index 100% rename from src/routes/concepts/components/event-handlers.mdx rename to old pages/concepts/components/event-handlers.mdx diff --git a/src/routes/concepts/components/props.mdx b/old pages/concepts/components/props.mdx similarity index 100% rename from src/routes/concepts/components/props.mdx rename to old pages/concepts/components/props.mdx diff --git a/src/routes/concepts/context.mdx b/old pages/concepts/context.mdx similarity index 100% rename from src/routes/concepts/context.mdx rename to old pages/concepts/context.mdx diff --git a/src/routes/concepts/control-flow/conditional-rendering.mdx b/old pages/concepts/control-flow/conditional-rendering.mdx similarity index 100% rename from src/routes/concepts/control-flow/conditional-rendering.mdx rename to old pages/concepts/control-flow/conditional-rendering.mdx diff --git a/src/routes/concepts/control-flow/data.json b/old pages/concepts/control-flow/data.json similarity index 100% rename from src/routes/concepts/control-flow/data.json rename to old pages/concepts/control-flow/data.json diff --git a/src/routes/concepts/control-flow/dynamic.mdx b/old pages/concepts/control-flow/dynamic.mdx similarity index 100% rename from src/routes/concepts/control-flow/dynamic.mdx rename to old pages/concepts/control-flow/dynamic.mdx diff --git a/src/routes/concepts/control-flow/error-boundary.mdx b/old pages/concepts/control-flow/error-boundary.mdx similarity index 100% rename from src/routes/concepts/control-flow/error-boundary.mdx rename to old pages/concepts/control-flow/error-boundary.mdx diff --git a/src/routes/concepts/control-flow/list-rendering.mdx b/old pages/concepts/control-flow/list-rendering.mdx similarity index 100% rename from src/routes/concepts/control-flow/list-rendering.mdx rename to old pages/concepts/control-flow/list-rendering.mdx diff --git a/src/routes/concepts/control-flow/portal.mdx b/old pages/concepts/control-flow/portal.mdx similarity index 100% rename from src/routes/concepts/control-flow/portal.mdx rename to old pages/concepts/control-flow/portal.mdx diff --git a/src/routes/concepts/data.json b/old pages/concepts/data.json similarity index 100% rename from src/routes/concepts/data.json rename to old pages/concepts/data.json diff --git a/src/routes/concepts/derived-values/data.json b/old pages/concepts/derived-values/data.json similarity index 100% rename from src/routes/concepts/derived-values/data.json rename to old pages/concepts/derived-values/data.json diff --git a/src/routes/concepts/derived-values/derived-signals.mdx b/old pages/concepts/derived-values/derived-signals.mdx similarity index 100% rename from src/routes/concepts/derived-values/derived-signals.mdx rename to old pages/concepts/derived-values/derived-signals.mdx diff --git a/src/routes/concepts/derived-values/memos.mdx b/old pages/concepts/derived-values/memos.mdx similarity index 100% rename from src/routes/concepts/derived-values/memos.mdx rename to old pages/concepts/derived-values/memos.mdx diff --git a/src/routes/concepts/effects.mdx b/old pages/concepts/effects.mdx similarity index 98% rename from src/routes/concepts/effects.mdx rename to old pages/concepts/effects.mdx index f1e2cd54a..f6c4631e4 100644 --- a/src/routes/concepts/effects.mdx +++ b/old pages/concepts/effects.mdx @@ -28,7 +28,7 @@ When the value of `count` changes, the effect is triggered, causing it to run ag Effects can be set to observe any number of dependencies. Dependencies are what allow an effect to track changes and respond accordingly. -These can include signals, variables, props, context, or any other reactive values. +These can include signals, props, context, or any other reactive values. When any of these change, the effect is notified and will run again to update its state. Upon initialization, an effect will run _once_, regardless of whether it has any dependencies. diff --git a/src/routes/concepts/intro-to-reactivity.mdx b/old pages/concepts/intro-to-reactivity.mdx similarity index 100% rename from src/routes/concepts/intro-to-reactivity.mdx rename to old pages/concepts/intro-to-reactivity.mdx diff --git a/src/routes/concepts/refs.mdx b/old pages/concepts/refs.mdx similarity index 100% rename from src/routes/concepts/refs.mdx rename to old pages/concepts/refs.mdx diff --git a/src/routes/concepts/signals.mdx b/old pages/concepts/signals.mdx similarity index 100% rename from src/routes/concepts/signals.mdx rename to old pages/concepts/signals.mdx diff --git a/src/routes/concepts/stores.mdx b/old pages/concepts/stores.mdx similarity index 92% rename from src/routes/concepts/stores.mdx rename to old pages/concepts/stores.mdx index 748ada6ae..36e30a01d 100644 --- a/src/routes/concepts/stores.mdx +++ b/old pages/concepts/stores.mdx @@ -3,9 +3,9 @@ title: Stores order: 6 --- -Similar to [signals](/concepts/signals), stores are a state management primitive. -However, while signals manage a single piece of state, stores create a centralized location to reduce code redundancy. -Within Solid, these stores can spawn a collection of reactive signals, each corresponding to a particular property which can be useful when working with complex state. +Stores are a state management primitive that provide a centralized way to handle shared data and reduce redundancy. +Unlike [signals](/concepts/signals), which track a single value and trigger a full re-render when updated, stores maintain fine-grained reactivity by updating only the properties that change. +They can produce a collection of reactive signals, each linked to an individual property, making them well-suited for managing complex state efficiently. ## Creating a store @@ -253,8 +253,10 @@ Instead of relying on discovering individual indices, path syntax introduces sev ### Appending new values -To append new values to an array in a store, use the setter function with the spread operator (`...`) to create a new array that includes the existing items and the new ones. -For appending a single element, you can instead leverage the "path syntax" by specifying the array’s length as the index to set. +To append values to an array in a store, use the setter function with the spread operator (`...`) or the path syntax. Both methods add an element to the array but differ in how they modify it and their reactivity behavior. + +The spread operator creates a new array by copying the existing elements and adding the new one, effectively replacing the entire `store.users` array. +This replacement triggers reactivity for all effects that depend on the array or its properties. ```jsx setStore("users", (otherUsers) => [ @@ -266,9 +268,12 @@ setStore("users", (otherUsers) => [ loggedIn: false, }, ]) +``` -// can become +The path syntax adds the new element by assigning it to the index equal to `store.users.length`, directly modifying the existing array. +This triggers reactivity only for effects that depend on the new index or properties like `store.users.length`, making updates more efficient and targeted. +```jsx setStore("users", store.users.length, { id: 3, username: "michael584", diff --git a/src/routes/concepts/understanding-jsx.mdx b/old pages/concepts/understanding-jsx.mdx similarity index 96% rename from src/routes/concepts/understanding-jsx.mdx rename to old pages/concepts/understanding-jsx.mdx index 8dea79978..6609b4c12 100644 --- a/src/routes/concepts/understanding-jsx.mdx +++ b/old pages/concepts/understanding-jsx.mdx @@ -11,7 +11,7 @@ This provides a concise and readable way to create and represent components. Solid was designed to align closely with HTML standards. -```jsx +```tsx const element =

I'm JSX!!

``` @@ -19,7 +19,7 @@ It offers a distinct advantage, however: to copy/paste solutions from resources Solid sets itself apart by using JSX immediately as it returns [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) elements. This lets you use dynamic expressions within your HTML by allowing variables and functions to be references with the use of curly braces (`{ }`): -```jsx +```tsx const Component = () => { const animal = { breed: "cat", name: "Midnight" } @@ -42,7 +42,7 @@ This updates only the necessary parts of the DOM when changes occur in the under Where HTML lets you have disconnected tags at the top level, JSX requires that a component to return a single root element. -:::advanced +:::note When working with JSX, parts of your code are translated into structured HTML that is placed at the start of the file. Static elements are processed differently from dynamic ones, which might change based on data or user actions. For dynamic elements, special markers are added for better handling during rendering. @@ -59,7 +59,7 @@ Self-closing tags are a must in JSX. Unlike in HTML, where elements like ``, ``, or `
` don't require explicit closure, JSX requires consistent self-closing tags. This helps to avoid potential rendering issues. -```jsx +```tsx ``` @@ -81,16 +81,17 @@ In JSX files, HTML attributes are used much like regular HTML, with a few key di (**Note:** When using ESLint, you will get a warning if you use lowercase.) - In cases where you can dynamically specify a value, you can replace the `"` and `"` with curly braces (`{ }`): -```jsx +```tsx ``` - :::note - If you wish to pass objects in JSX, such as with inline styling, you will have to use double curly braces (`{{ }}`). +:::note + +If you wish to pass objects in JSX, such as with inline styling, you will have to use double curly braces (`{{ }}`). -```jsx +```tsx + + +``` + +You can also use JavaScript expressions to set attribute values dynamically: + +```tsx +const isActive = true; + + +``` + +For more information on: + +- [Using Event Listeners](/components/how-to/event-listeners) +- [Styling Components](/components/how-to/styling) + +#### JSX Properties (Props) + +JSX properties (or props) are how you pass data and event handlers to components in Solid. +They work similarly to HTML attributes but can accept JavaScript expressions. + +In this example, the `name` prop is passed to the `Greeting` component, which uses it within its JSX: + +```tsx +function App() { + return ( +
+ + +
+ ); +} + +function Greeting(props) { + return

Hello, {props.name}!

; +} +``` + +The core concepts of JSX properties in Solid include: + +- **Static props:** Directly integrated into the HTML by cloning the template and using them as attributes. +- **Dynamic props:** Rely on state, allowing content or properties to change in response to user interactions or other events. +An example is changing the style of an element based on a signal (`value={value()}`). +- **Data transfer:** Props can be used to fill components with data from resources, such as API responses or context providers. + +This allows for components to update reactively when the underlying data changes. + +:::note +In JSX, expressions are applied in the order they appear. +This works for most elements, but some (i.e. ``) require attributes in a specific order. +When order matters, make sure to define expressions in the sequence the element expects. +::: + +## Related Pages + +- [Using Event Listeners](/components/how-to/event-listeners) +- [Styling Components](/components/how-to/styling) +- [Passing Data with Props](/components/props) diff --git a/src/routes/guides/data.json b/src/routes/guides/data.json index 364f3f925..88ddc06cb 100644 --- a/src/routes/guides/data.json +++ b/src/routes/guides/data.json @@ -1,14 +1,4 @@ { "title": "Guides", - "pages": [ - "styling-your-components.mdx", - "styling-components", - "state-management.mdx", - "routing-and-navigation.mdx", - "complex-state-management.mdx", - "fetching-data.mdx", - "testing.mdx", - "deploying-your-app.mdx", - "deployment-options" - ] + "pages": [] } diff --git a/src/routes/index.mdx b/src/routes/index.mdx index bc137a50e..2be7028b9 100644 --- a/src/routes/index.mdx +++ b/src/routes/index.mdx @@ -3,6 +3,8 @@ title: Overview mainNavExclude: true --- +[TODO: Convert to landing page. Move any relevant info into Solid Info] + # Overview Solid is a modern JavaScript framework designed to build responsive and high-performing user interfaces (UI). diff --git a/src/routes/intro.mdx b/src/routes/intro.mdx new file mode 100644 index 000000000..e7944c791 --- /dev/null +++ b/src/routes/intro.mdx @@ -0,0 +1,35 @@ +--- +title: Intro to Solid +order: 1 +--- + +[TODO: +- Input on what should be included in a Solid intro +- Answer questions such as: (maybe) + - What are the key features of Solid? + - Why use + - What are the benefits of using Solid? + - Ecosystem +] + +Solid is a modern JavaScript framework designed to build responsive and high-performing user interfaces. +It prioritizes a simple, yet predictable, developer experience, making it a great choice for developers of all skill levels. + +## What is Solid? + +As a JavaScript framework, Solid embraces reactivity and fine-grained updates. +What this means is that Solid can efficiently update the user interface by only re-rendering the parts of the UI that have changed, rather than the entire page. + +Traditionally, when a change occurs, the entire page would need to reload to display the updated information. +With Solid's fine-grained reactive system, updates are only applied to the parts of the page that need to be updated. +This decreases work and can result in faster load times as well as a smoother user experience overall. + +## The mental model + +[TODO: Simple explanation of the mental model behind Solid] + +## Where to go next + +- [Quick Start Guide](https://solidjs.com/docs/getting-started) +- [Reactivity Overview](/concepts/reactivity/) +- [Component Overview](/concepts/components/) \ No newline at end of file diff --git a/src/routes/reactivity/basics.mdx b/src/routes/reactivity/basics.mdx new file mode 100644 index 000000000..4cd25da77 --- /dev/null +++ b/src/routes/reactivity/basics.mdx @@ -0,0 +1,92 @@ +--- +title: "Reactivity Basics" +order: 1 +--- + + +Reactivity is the foundation of Solid. +It’s the programming model where **changes in data automatically update the parts of your app that depend on it**. + +Solid’s approach is **fine-grained**: only the exact parts of the DOM that depend on a value update. +This makes applications efficient and responsive, even as they grow. + +## Core principles of reactivity + +Solid’s reactivity is built on three key ideas: **signals, subscribers, and tracking scopes**. +Together, these create a system where updates are precise, automatic, and efficient. + +### Signals + +A **signal** is a reactive value container. +They consist of 2 parts: + +- **Getter** → reads the current value. +- **Setter** → updates the value and notifies dependents. + +```tsx +const [count, setCount] = createSignal(0); + +console.log(count()); // 0 +setCount(1); +console.log(count()); // 1 +``` + +Signals can hold any type of value: + +- Primitives (string, number, boolean). +- Objects and arrays. + +Learn more in the [Signals page](/reactivity/signals). + +### Subscribers + +If signals are the **source of truth**, subscribers are the **reactive consumers**. + +A subscriber is any function or construct that reads a signal inside a tracking scope and automatically re-runs whenever it changes. +This is what makes Solid’s reactivity *fine-grained*: only the subscribers that depend on a signal update. + +How subscribers work: + +1. **Observation** → subscriber runs and reads signals. +2. **Dependency tracking** → those signals register the subscriber. +3. **Response** → when a signal changes, the subscriber re-runs. + +### Tracking Scopes + +A **tracking scope** is the environment where Solid records which signals are being used. +When a signal is read within a tracking scope, it registers the current subscriber as a dependency. +Once that signal changes, it will notify the subscriber to re-run and update accordingly. + +Tracking scopes within Solid include: + +- Component render functions (JSX return values). +- Reactive computations (e.g., `createMemo`). +- Effects (e.g., `createEffect`) + +Example of a tracking vs non-tracking scope: + +```tsx +import { createSignal } from "solid-js"; + +function Counter() { + const [count, setCount] = createSignal(0); + + console.log(count()) // ❌ Not tracked — runs once + + return ( +
+

Count: {count()}

{/* âś… tracked automatically - re-runs on update */} + +
+ ); +} +``` + +Tracking scopes are what enable **precise DOM updates**: only parts of the UI that depend on signals re-run. +You can learn more about [component lifecycles in the Intro to Components page](/components/intro). + + +## Related pages +- [Signals](/reactivity/signals) +- [Effects](/reactivity/effects) +- [Introduction to Components](/components/intro) diff --git a/src/routes/reactivity/data.json b/src/routes/reactivity/data.json new file mode 100644 index 000000000..7eacc5dcb --- /dev/null +++ b/src/routes/reactivity/data.json @@ -0,0 +1,12 @@ +{ + "title": "Reactivity", + "pages": [ + "basics.mdx", + "signals.mdx", + "derived-state.mdx", + "effects.mdx", + "stores.mdx", + "resources.mdx", + "how-to" + ] +} diff --git a/src/routes/reactivity/derived-state.mdx b/src/routes/reactivity/derived-state.mdx new file mode 100644 index 000000000..106ce7bdc --- /dev/null +++ b/src/routes/reactivity/derived-state.mdx @@ -0,0 +1,65 @@ +--- +title: "Derived State" +order: 3 +--- + +Derived state often refers to values that are computed based on other reactive values. +Rather than storing multiple pieces of state and manually keeping them consistent, Solid encourages you to derive new values from existing signals, memos, or stores. + +The benefits of derived state: +- You store only the minimal amount of state necessary. +- Relationships between values are described declaratively, making it easier to understand how data flows through your application. +- Derived values are automatically kept in sync with their dependencies. + +## Derived signals + +The simplest way to create derived state is by using a function that accesses one or more signals. + +```tsx +const [count, setCount] = createSignal(0); + +// A derived signal: always reflects count * 2 +const double = () => count() * 2; + +console.log(double()); // 0 +setCount(5); +console.log(double()); // 10 +``` + +In the above example, `double` is a derived signal that computes its value based on the `count` signal. +Whenever `count` changes, calling `double()` produces the correct and current value. +Any component or effect that uses `double()`, too, will update automatically when `count` changes. + +### Limitations of derived signals + +While derived signals are simple and effective for many use cases, they have some limitations: + +- **They do not cache their results.** + For derived signal call, the function is re-executed every time it is accessed, even if the underlying signals have not changed. + For trivial computations, this is not a problem, but for expensive calculations (eg. processing large datasets), this can lead to performance issues. +- **They do not create their own reactive scope.** + A derived function runs only when you call it. + Reactivity comes from the signals it touches, but there is no intermediate node in the reactivity graph. +- **Dependent computations can cause unnecessary updates.** + If a derived signal is used within another derived signal or effect, any change to its dependencies will trigger the entire chain to re-evaluate, even if the final value does not change. + +In other words, derived signals are best suited for lightweight computations, but they may not be ideal for more complex or performance-sensitive scenarios. + +## Memoizing derived state + +Memos are a specialized type of derived state that address the limitations of simple derived signals. +They cache results and only re-compute when their dependencies change, making them much more efficient for expensive or frequently accessed calculations. + +```tsx +// add example +``` + + +### Advantages of memos + +Memos offer several advantages over simple derived signals: + +- **Efficient execution** – a memo runs only once per dependency change, rather than re-executing every time it’s accessed. +- **Cached results** – results are stored and reused, avoiding unnecessary recomputation of expensive logic. +- **Change detection** – if dependencies change but the computed value is strictly equal (`===`) to the previous result, no downstream updates are triggered. +- **Automatic tracking** – any signal or memo read inside a memo’s function is automatically tracked, so the memo re-evaluates only when those dependencies change. \ No newline at end of file diff --git a/src/routes/reactivity/effects.mdx b/src/routes/reactivity/effects.mdx new file mode 100644 index 000000000..17936e4d9 --- /dev/null +++ b/src/routes/reactivity/effects.mdx @@ -0,0 +1,165 @@ +--- +title: "Effects" +order: 4 +--- + +Effects are reactive functions that run when the values they depend on change. +They’re the main way to manage side effects that occur outside an application's scope, such as: +- DOM manipulation +- Data fetching +- Subscriptions + +## What are Effects? + +An effect is a reactive function that **runs automatically whenever the values it depends on change**. + +Unlike [signals](/basics/signals) (which store state) or [memos](/basics/derived-state) (which derive state), effects connect your reactive data to the outside world. +They are used for running code that has side effects, meaning it interacts with or modifies something beyond Solid's reactive graph, such as: + +- updating or interacting with the DOM +- logging values +- fetching or subscribing to external data +- setting up or cleaning up resources + +Effects run **once when created** to establish its subscriptions, then **again whenever its dependencies change**. + +## Creating an Effect + +You create an effect using the `createEffect` function. +It takes a **callback function** as its first argument, which contains the code you want to run reactively. + +```tsx +import { createSignal, createEffect } from "solid-js"; + +function Counter() { + const [count, setCount] = createSignal(0); + + createEffect(() => { + console.log(`Count is: ${count()}`); + }); + + return ( + + ); +} +``` + +In this example, the effect logs the current value of `count` to the console. +The effect runs once when created, logging `Count is: 0`, and will run again whenever `count` changes, in this case when the button is clicked. + +## Lifecycle of an Effect + +An effect goes through a predictable cycle every time it's created and re-runs: + +### 1. **Initialization** + +When you call `createEffect`, the effect is immediately scheduled to run **once**. +This first run happens **after the current render phase** finishes, or after the component function returns its JSX. +This ensures that the DOM is already created and refs are assigned, allowing you to safely interact with the DOM. + +```tsx +createEffect(() => console.log("Initial run")); +console.log("Hello"); + +// Output: +// Hello +// Initial run +``` + +### 2. **Dependency tracking** + +During its first run, the effect records every reactive value it accesses (such as signals or memos). +It becomes subscribed to those values, which are now its **dependencies**. +From then on, the effect automatically re‑runs whenever any of its dependencies change. + +```tsx +const [count, setCount] = createSignal(0); + +createEffect(() => { + console.log(`Count: ${count()}`); +}); + +// Count: 0 runs immediately +// every setCount(...) re‑runs the effect +``` + +### 3. **Reactive Updates** + +Whenever *any* dependency changes, the effects is scheduled to run again. +This happens **after the current synchronous code completes**, ensuring that all changes are batched together. +This means that if the dependencies change multiple times in quick succession, the effect will only run *once* after all changes are made: + +```tsx +const [count, setCount] = createSignal(0); +const [name, setName] = createSignal("Solid"); + +createEffect(() => { + console.log(`Count: ${count()}, Name: ${name()}`); +}); + +setCount(1); +setName("SolidJS"); +``` + +In this example, the effect will only log `Count: 1, Name: SolidJS` once, after all changes have been made. + +### Lifecycle functions + +Effects are reactive and run whenever their dependencies change, but sometimes finer control is needed. +Solid provides lifecycle functions to manage when code runs and how it gets cleaned up. + +These functions are especially useful for: +- Running a side effect **only once** when the component mounts +- Cleaning up resources, event listeners, or other side effects when the component unmounts + +#### `onMount` + +The [`onMount`](TODO) function allows you to run code **once** when the component is first added to the DOM. +Unlike effects, `onMount` does **not track dependencies**. +It will execute the callback once, and never again. + +```tsx +import { onMount, createSignal, createEffect } from "solid-js"; + +function Component() { + const [data, setData] = createSignal(null); + + createEffect(() => { + // Still runs reactively whenever `data` changes + console.log("Data:", data()); + }); + + onMount(async () => { + // Runs only once when the component is mounted + const res = await fetch("/api/data"); + setData(await res.json()); + }); + + return
...
; +} +``` + +#### `onCleanup` + +Use [`onCleanup`](TODO) to **dispose of resources** before a component unmounts, or before an effect re-runs. +This will prevent memory leaks and ensure that any subscriptions or event listeners are properly removed. + +```tsx +import { createSignal, onCleanup } from "solid-js"; + +function App() { + const [count, setCount] = createSignal(0); + + const timer = setInterval(() => setCount(c => c + 1), 1000); + + onCleanup(() => { + // Runs when the component unmounts + clearInterval(timer); + }); + + return
Count: {count()}
; +} +``` + diff --git a/src/routes/reactivity/how-to/async-data.mdx b/src/routes/reactivity/how-to/async-data.mdx new file mode 100644 index 000000000..f8ba4a246 --- /dev/null +++ b/src/routes/reactivity/how-to/async-data.mdx @@ -0,0 +1,8 @@ +--- +title: "Async Data" +order: 2 +--- + +[TODO: +How to work with async Data +] \ No newline at end of file diff --git a/src/routes/reactivity/how-to/data.json b/src/routes/reactivity/how-to/data.json new file mode 100644 index 000000000..53d08c540 --- /dev/null +++ b/src/routes/reactivity/how-to/data.json @@ -0,0 +1,4 @@ +{ + "title": "How To", + "pages": ["derived-signals.mdx", "async-data.mdx", "debug-reactivity.mdx"] +} diff --git a/src/routes/reactivity/how-to/debug-reactivity.mdx b/src/routes/reactivity/how-to/debug-reactivity.mdx new file mode 100644 index 000000000..28ad0f70b --- /dev/null +++ b/src/routes/reactivity/how-to/debug-reactivity.mdx @@ -0,0 +1,4 @@ +--- +title: "Debugging Reactivity" +order: 3 +--- diff --git a/src/routes/reactivity/how-to/derived-signals.mdx b/src/routes/reactivity/how-to/derived-signals.mdx new file mode 100644 index 000000000..59dd72745 --- /dev/null +++ b/src/routes/reactivity/how-to/derived-signals.mdx @@ -0,0 +1,11 @@ +--- +title: "Derived Signals" +order: 1 +--- + +[TODO: +How to create derived signals +- when to use +- how to use +- examples +] \ No newline at end of file diff --git a/src/routes/reactivity/memos.mdx b/src/routes/reactivity/memos.mdx new file mode 100644 index 000000000..bdaf190ab --- /dev/null +++ b/src/routes/reactivity/memos.mdx @@ -0,0 +1,11 @@ +--- +title: "Memos" +order: 3 +--- + +[TODO: +Concept page on Memos +- Improve on existing page +- Move relevant sections to reference and vice versa +- Link out to how-to for derived signals where appropriate +] \ No newline at end of file diff --git a/src/routes/reactivity/overview.mdx b/src/routes/reactivity/overview.mdx new file mode 100644 index 000000000..f6351bcd0 --- /dev/null +++ b/src/routes/reactivity/overview.mdx @@ -0,0 +1,69 @@ +--- +title: "Overview" +order: 1 +--- + +[TODO: Review] + +:::note +While this guide can be helpful for understanding reactive systems, it does use some Solid-specific terminology and concepts. +::: + +Reactivity is what powers the interactivity of Solid apps. +This programming paradigm refers to a system's ability to respond to changes in data, or state, automatically and efficiently. +Solid is built with reactivity at the core of its design, assisting applications with staying up-to-date with its underlying data. + +## The Importance of Reactivity + +Reactivity is what keeps the user interface (UI) in sync with the underlying application state. +When the state changes, the UI is automatically updated, reducing the need for manual updates. + +In addition, reactivity enables real-time updates, allowing applications to reflect changes instantly without requiring a full page refresh. +This helps with creating more responsive and interactive user experiences. + +As an example, when building a Counter, you can use the reactive primitives provided by Solid to create a counter. +Once the counter is set up, whenever the count changes, the UI will automatically update to reflect the new count: + +```tsx +function Counter() { + const [count, setCount] = createSignal(0); + const increment = () => setCount((prev) => prev + 1); + + return ( +
+ Count: {count()}{" "} + {/* Only `count()` is updated when the button is clicked. */} + +
+ ); +} +``` + +## Reactive Principles + +### Signals + +Signals serve as the core elements within a reactive system. +They play a crucial role in tracking and managing state changes, allowing the UI to respond automatically when the underlying data changes. +They are responsible for storing and managing data, as well as triggering updates across the system. + +Signals are able to achieve this reactivity through the use of: + +- **Getters**: A function responsible for retrieving the current value of a signal. +When called within a reactive context, it will give access to the current value of the signal. +- **Setters**: This function is responsible for updating the value of a signal. +To trigger reactivity, setters notify the system that the signal's value has changed, prompting anything that depends on the signal to re-evaluate and update accordingly. + +### Subscribers + +Subscribers refer to other reactive contexts or components that depend on the value of a signal. +These automated responders keep the system up-to-date by re-evaluating and updating whenever the signal's value changes. + +Subscribers work based on two main actions: +- **Observation**: The core function of a subscriber is to observe signals. +This keeps the subscriber informed about any changes to the signal(s) they're tracking. +- **Respond**: Once the signal has updated and the subscriber is notified, it triggers a re-evaluation of the dependent computations or UI updates. + +[TODO: Some kind of image / code block??] \ No newline at end of file diff --git a/src/routes/reactivity/resources.mdx b/src/routes/reactivity/resources.mdx new file mode 100644 index 000000000..16342dad1 --- /dev/null +++ b/src/routes/reactivity/resources.mdx @@ -0,0 +1,8 @@ +--- +title: "Handling Async Data" +order: 6 +--- + +[TODO: +Concept page on Resource +] \ No newline at end of file diff --git a/src/routes/reactivity/signals.mdx b/src/routes/reactivity/signals.mdx new file mode 100644 index 000000000..e19c1e3c3 --- /dev/null +++ b/src/routes/reactivity/signals.mdx @@ -0,0 +1,73 @@ +--- +title: "Signals" +order: 2 +--- + +Signals are the **primary way to manage state** in a Solid application. +They store values, notify dependents when those values change, and form the [foundation of reactivity](/reactivity/overview). + +You can use signals for any kind of state: +- simple values (strings, numbers) +- complex values (objects, arrays) +- application state (current user, theme, page, etc.) + +## What is a Signal? + +A **signal** is a reactive value container. +It holds a value and provides a **getter** to read the value and a **setter** to update it. +When the value changes, it automatically triggers updates in any dependent computations. + +They can be thought of as a **reactive variable**. + +## Create a Signal + + +Use [`createSignal`](/reference/basic-reactivity/create-signal) from `solid-js`: + +```tsx +import { createSignal } from "solid-js"; + +const [count, setCount] = createSignal(0); +// ^ getter ^ setter +``` + +:::note +The [getter, setter] syntax uses [array destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)(). +::: + + +## Read a Signal's Value + +To read the current value of a signal, simply call the getter function: + +```tsx +console.log(count()); // 0 +``` + +## Update a Signal's Value + +To update the value of a signal, call the setter function with the new value: + +```tsx +setCount(1); +console.log(count()); // 1 +``` + +## Reactivity in Action + +Signals are **reactive**: when used inside a tracking scope, they automatically notify dependents when updated. + +```tsx +function Counter() { + const [count, setCount] = createSignal(0); + const increment = () => setCount(prev => prev + 1); + + return ( +
+ Count: {count()} {/* Updates when `count` changes */} + +
+ ); +} +``` + diff --git a/src/routes/reactivity/stores.mdx b/src/routes/reactivity/stores.mdx new file mode 100644 index 000000000..5a5384762 --- /dev/null +++ b/src/routes/reactivity/stores.mdx @@ -0,0 +1,100 @@ +--- +title: "Stores" +order: 5 +--- + +Stores are a state management primitive in Solid that provide a convenient, centralized way to handle structured data. +Unlike signals, which are best suited for primitive values, stores are better for managing complex data structures like objects and arrays: + +- They provide fine-grained reactivity, meaning only the parts of the store that are accessed will trigger updates when they change. +- They support **nested reactivity,** so you can track and update deeply nested properties without losing reactivity. +- They make working with complex state easier while avoiding duplication or unnecessary recomputation. + +Stores are built on top of Solid's reactive system, leveraging [JavaScript's proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to track property access and changes. + +## Creating a Store + +To create a store, you can use the `createStore` function from the `solid-js/store` package. +This function takes an initial value and returns a tuple containing the store and a setter function to update it. + +```tsx +import { createStore } from "solid-js/store" + +// Initialize store +const [store, setStore] = createStore({ + userCount: 3, + users: [ + { + id: 0, + username: "felix909", + location: "England", + loggedIn: false, + }, + { + id: 1, + username: "tracy634", + location: "Canada", + loggedIn: true, + }, + { + id: 2, + username: "johny123", + location: "India", + loggedIn: true, + }, + ], +}) +``` + +## Accessing store values + +Unlike signals, which are functions that need to be called to get their values, stores can be accessed directly like regular objects or arrays. + +```tsx +console.log(store.userCount) // 3 +``` + +Inside a tracking scope, store properties can be accessed directly, and any changes to those properties will trigger updates to the scope. + +:::note + +Stores create signals **lazily**. +This means that a property only becomes reactive once accessed inside a tracking scope. +::: + +```tsx +const App = () => { + const [mySignal, setMySignal] = createSignal("This is a signal.") + const [store, setStore] = createStore({ + userCount: 3, + users: [ + { + id: 0, + username: "felix909", + location: "England", + loggedIn: false, + }, + { + id: 1, + username: "tracy634", + location: "Canada", + loggedIn: true, + }, + { + id: 2, + username: "johny123", + location: "India", + loggedIn: true, + }, + ], + }) + return ( +
+

Hello, {store.users[0].username}

{/* Accessing a store value */} + {mySignal()} {/* Accessing a signal */} +
+ ) +} +``` + +## \ No newline at end of file diff --git a/src/routes/reference/basic-reactivity/create-effect.mdx b/src/routes/reference/basic-reactivity/create-effect.mdx index d93501967..8a201215e 100644 --- a/src/routes/reference/basic-reactivity/create-effect.mdx +++ b/src/routes/reference/basic-reactivity/create-effect.mdx @@ -35,7 +35,7 @@ const [a, setA] = createSignal(initialValue) // effect that depends on signal `a` createEffect((prevSum) => { // do something with `a` and `prevSum` - const sum = a() + b() + const sum = a() + prevSum if (sum !== prevSum) console.log("sum changed to", sum) return sum }, 0) diff --git a/src/routes/reference/jsx-attributes/use.mdx b/src/routes/reference/jsx-attributes/use.mdx index b622f82c2..ebfc504f4 100644 --- a/src/routes/reference/jsx-attributes/use.mdx +++ b/src/routes/reference/jsx-attributes/use.mdx @@ -3,38 +3,91 @@ title: use:* order: 5 --- -These are custom directives. In a sense this is just syntax sugar over ref but allows us to easily attach multiple directives to a single element. A directive is a function with the following signature: +Custom directives attach reusable behavior to DOM elements, acting as syntactic sugar over `ref`. They’re ideal for complex DOM interactions like scrolling, tooltips, or form handling, which are cumbersome to repeat in JSX. + +A directive is a function with the following signature ```ts -function directive(element: Element, accessor: () => any): void +function directive(element: HTMLElement, accessor: Accessor): void; ``` Directive functions are called at render time but before being added to the DOM. You can do whatever you'd like in them including create signals, effects, register clean-up etc. +## Example + +A `model` directive for two-way data binding + ```tsx -const [name, setName] = createSignal("") +import type { Accessor, Signal } from "solid-js"; -function model(el, value) { - const [field, setField] = value() - createRenderEffect(() => (el.value = field())) - el.addEventListener("input", (e) => setField(e.target.value)) -}; +function model(element: HTMLInputElement, value: Accessor>) { + const [field, setField] = value(); + createRenderEffect(() => (element.value = field())); + element.addEventListener("input", ({ target }) => setField(target.value)); +} + +const [name, setName] = createSignal(""); - +; ``` -To register with TypeScript extend the JSX namespace. +## TypeScript Support + +To type custom directives, extend the `DirectiveFunctions` interface ```ts +declare module "solid-js" { + namespace JSX { + interface DirectiveFunctions { + model: typeof model; + } + } +} +``` + +If you just want to constrain the second argument to the directive function, you can extend the older `Directives` interface + +```tsx declare module "solid-js" { namespace JSX { interface Directives { - model: [() => any, (v: any) => any] + model: Signal; } } } ``` +## Avoiding Tree-Shaking + +When importing a directive `d` from another module and using it only as `use:d`, TypeScript (via [babel-preset-typescript](https://babeljs.io/docs/babel-preset-typescript)) may remove the import, as it doesn’t recognize `use:d` as a reference to `d`. +To prevent this: + +1. Use the `onlyRemoveTypeImports: true` option in `babel-preset-typescript`. For `vite-plugin-solid`, add this to `vite.config.ts` + + ```ts + import solidPlugin from "vite-plugin-solid"; + + export default { + plugins: [ + solidPlugin({ + typescript: { onlyRemoveTypeImports: true } + }) + ], + }; + ``` + + Note: This requires consistent use of `export type` and `import type` in your codebase to avoid issues. + +2. Add a fake access like `false && d;` in the module + + ```tsx + import { model } from "./directives"; + false && model; // Prevents tree-shaking + ; + ``` + + This is removed by bundlers like Terser, unlike a plain `model;` which may remain in the bundle. + :::caution[Limitations] Directives only work with native HTML elements (HTML/SVG/MathML/Custom Elements). Directives are not forwarded and **won't work in user defined components**, such as `` [see also](https://github.com/solidjs/solid/discussions/722) diff --git a/src/routes/reference/reactive-utilities/split-props.mdx b/src/routes/reference/reactive-utilities/split-props.mdx index 3841792ce..7dfcbe18f 100644 --- a/src/routes/reference/reactive-utilities/split-props.mdx +++ b/src/routes/reference/reactive-utilities/split-props.mdx @@ -37,7 +37,7 @@ Because `splitProps` takes any number of arrays, we can split a props object as Let's say a component was passed six props: ```tsx -; + // ... function MyComponent(props) { diff --git a/src/routes/rendering/data.json b/src/routes/rendering/data.json new file mode 100644 index 000000000..1fbb9d7ea --- /dev/null +++ b/src/routes/rendering/data.json @@ -0,0 +1,4 @@ +{ + "title": "Rendering", + "pages": [] +} diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index 53a96da9c..3d59eb63b 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -2,151 +2,445 @@ title: "Actions" --- -When developing applications, it is common to need to communicate new information to the server based on user interactions. -Actions are Solid Router’s solution to this problem. +Many user interactions in an application involve changing data on the server. +These **mutations** can be challenging to manage, as they require updates to the application's state and proper error handling. +Actions simplify managing data mutations. -## What are actions? +Actions provide several benefits: -Actions are asynchronous processing functions that allow you to submit data to your server and receive a response. -They are isomorphic, meaning they can run either on the server or the client, depending on what is needed. -This flexibility makes actions a powerful tool for managing and tracking data submissions. +- **Integrated state management:** + Solid Router automatically tracks the execution state of an action, simplifying reactive UI feedback. +- **Automatic data revalidation:** + After an action successfully completes, Solid Router revalidates relevant [`queries`](/solid-router/concepts/queries), ensuring the UI reflects the latest data. +- **Progressive enhancement:** + When used with HTML forms, actions enable functionality even if JavaScript is not yet loaded. -### How actions work +## Defining actions -Actions represent the server-side part of an [HTML form](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form). -They handle submissions through POST requests, allowing you to easily use HTML forms to send data. +Actions are defined by wrapping the data-mutation logic with the [`action` function](/solid-router/reference/data-apis/action). -When a user performs an action, such as submitting a form, the data is sent to the server for processing via an action. +```tsx +import { action } from "@solidjs/router"; + +const createTicketAction = action(async (subject: string) => { + const response = await fetch("https://my-api.com/support/tickets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ subject }), + }); + + if (!response.ok) { + const errorData = await response.json(); + return { ok: false, message: errorData.message }; + } + + return { ok: true }; +}, "createTicket"); +``` + +In this example, an action is defined that creates a support ticket using a remote API. + +## Using actions + +Actions can be triggered in two ways: using a HTML [`
` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form) or programmatically using the [`useAction` primitive](/solid-router/reference/data-apis/use-action). -### Benefits of using actions +The recommended approach is to use a `` element. +This ensures a robust user experience with progressive enhancement, since the form works even without JavaScript. -1. **Isomorphic**: Since actions can run on both the server and client, you can optimize performance by choosing the best execution environment for your needs. -2. **Asynchronous processing**: Actions handle data submissions asynchronously, ensuring that your application remains responsive. -3. **Simplified data handling**: By using actions, the process of managing and tracking data submissions can be streamlined, reducing the complexity of your application. +For cases where a form is not suitable, the [`useAction` primitive](/solid-router/reference/data-apis/use-action) can be used to trigger the action programmatically. -## Creating actions +### With the `` element -To create an action, use the `action` function from the `@solidjs/router` package. -This function takes an asynchronous function as an argument and returns a new function that can be used to submit data. +Solid Router extends the standard HTML `` element to work with actions. +Form submissions can be handled using action by passing an action to the `action` prop. + +Consider these points when using actions with ``: + +1. The `` element **must** have `method="post"`. +2. The action function will automatically receive the form's data as a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object as its first parameter. +3. For SSR environments, a unique name **must** be provided as the second parameter to the `action` function. + This name is used by Solid Router to identify and serialize the action across the client and server. ```tsx import { action } from "@solidjs/router"; -const echo = action(async (message: string) => { - // Simulates an asynchronous operation, such as an API call - await new Promise((resolve, reject) => setTimeout(resolve, 1000)); - console.log(message); -}); +const submitFeedbackAction = action(async (formData: FormData) => { + const message = formData.get("message")?.toString(); + // ... Sends the feedback to the server. +}, "submitFeedback"); + +function FeedbackForm() { + return ( + +