|
| 1 | +--- |
| 2 | +title: "Intro to reactivity" |
| 3 | +order: 1 |
| 4 | +--- |
| 5 | + |
| 6 | +**Note**: While this guide is useful for understanding reactive systems, it does use some Solid-specific terminology. |
| 7 | + |
| 8 | +Reactivity powers the interactivity in Solid applications. |
| 9 | +This programming paradigm refers to a system's ability to respond to changes in data or state automatically. |
| 10 | +With Solid, reactivity is the basis of its design, ensuring applications stay up-to-date with their underlying data. |
| 11 | + |
| 12 | +## Importance of reactivity |
| 13 | + |
| 14 | +1. Reactivity keeps the user interface (UI) and state in sync, which reduces the need for manual updates. |
| 15 | + |
| 16 | +2. Real-time updates create a more responsive and interactive user experience. |
| 17 | + |
| 18 | +```jsx |
| 19 | +function Counter() { |
| 20 | + const [count, setCount] = createSignal(0); |
| 21 | + const increment = () => setCount((prev) => prev + 1); |
| 22 | + |
| 23 | + return ( |
| 24 | + <div> |
| 25 | + <span>Count: {count()}</span>{" "} |
| 26 | + {/* Only `count()` is updated when the button is clicked. */} |
| 27 | + <button type="button" onClick={increment}> |
| 28 | + Increment |
| 29 | + </button> |
| 30 | + </div> |
| 31 | + ); |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +This `Counter` function sets up a button that, when clicked, calls the `increment` function to increase the `count` by one. |
| 36 | +This updates just the number displayed _without_ refreshing the entire component. |
| 37 | + |
| 38 | +<EraserLink |
| 39 | + href="https://app.eraser.io/workspace/maDvFw5OryuPJOwSLyK9?elements=cry9JT4nroFQ4rRxzOpvCg" |
| 40 | + preview="https://app.eraser.io/workspace/maDvFw5OryuPJOwSLyK9/preview?elements=cry9JT4nroFQ4rRxzOpvCg&type=embed" |
| 41 | +/> |
| 42 | + |
| 43 | +## Reactive principles |
| 44 | + |
| 45 | +### Signals |
| 46 | + |
| 47 | +Signals serve as core elements in reactive systems, playing an important role in data management and system responsiveness. |
| 48 | +They are responsible for storing and managing data, as well as triggering updates across the system. |
| 49 | +This is done through the use of getters and setters. |
| 50 | + |
| 51 | +```jsx |
| 52 | +const [count, setCount] = createSignal(0); |
| 53 | +// ^ getter ^ setter |
| 54 | +``` |
| 55 | + |
| 56 | +<EraserLink |
| 57 | + href="https://app.eraser.io/workspace/maDvFw5OryuPJOwSLyK9?elements=lseAEjGlKLslaVsTlfej_g" |
| 58 | + preview="https://app.eraser.io/workspace/maDvFw5OryuPJOwSLyK9/preview?elements=lseAEjGlKLslaVsTlfej_g&type=embed" |
| 59 | +/> |
| 60 | + |
| 61 | +- **Getter**: A function responsible for accessing the current value of the signal. |
| 62 | + You call a getter to access the data stored in a signal within a component. |
| 63 | + |
| 64 | +- **Setter**: |
| 65 | + The function used to modify a signal's value. |
| 66 | + To trigger reactive updates across an application, you call a setter to update the value of a signal. |
| 67 | + |
| 68 | +```js |
| 69 | +console.log(count()); // `count()` is a getter that returns the current value of `count`, which is `0`. |
| 70 | + |
| 71 | +setCount(1); // the setter, `setCount`, updates the value of `count`. |
| 72 | + |
| 73 | +console.log(count()); // the updated value of `count` is now `1`. |
| 74 | +``` |
| 75 | + |
| 76 | +### Subscribers |
| 77 | + |
| 78 | +Subscribers are the other core element in reactive systems. |
| 79 | +They are responsible for tracking changes in signals and updating the system accordingly. |
| 80 | +They are automated responders that keep the system up-to-date with the latest data changes. |
| 81 | + |
| 82 | +Subscribers work based on two main actions: |
| 83 | + |
| 84 | +- **Observation**: At their core, subscribers observe signals. |
| 85 | + This keeps the subscriber primed to pick up on any changes to the signal they are tracking. |
| 86 | +- **Response**: When a signal changes, the subscriber is notified. |
| 87 | + This triggers the subscriber to respond to the change in the signal. |
| 88 | + This can involve tasks like updating the UI or calling external functions. |
| 89 | + |
| 90 | +```jsx |
| 91 | +function Counter() { |
| 92 | + const [count, setCount] = createSignal(0); |
| 93 | + const increment = () => setCount((prev) => prev + 1); |
| 94 | + |
| 95 | + createEffect(() => { |
| 96 | + console.log(count()); |
| 97 | + }); |
| 98 | + // the `createEffect` will trigger the console log every time `count` changes. |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +## State management |
| 103 | + |
| 104 | +State management is the process of managing the state of an application. |
| 105 | +This involves storing and updating data, as well as responding to the changes in it. |
| 106 | + |
| 107 | +With Solid, state management is handled through signals and subscribers. |
| 108 | +Signals are used to store and update data, while subscribers are used to respond to changes in the data. |
| 109 | + |
| 110 | +### Tracking changes |
| 111 | + |
| 112 | +Tracking changes involves monitoring any updates to the data and responding accordingly. |
| 113 | +This is done through the use of subscribers. |
| 114 | + |
| 115 | +When a signal is not accessed within a tracking scope, an update to the signal will not trigger an update. |
| 116 | +This happens because if a signal is not being tracked, it is not able to notify any subscribers of the change. |
| 117 | + |
| 118 | +```jsx |
| 119 | +const [count, setCount] = createSignal(0); |
| 120 | + |
| 121 | +console.log("Count:", count()); |
| 122 | + |
| 123 | +setCount(1); |
| 124 | + |
| 125 | +// Output: Count: 0 |
| 126 | + |
| 127 | +// `count` is not being tracked, so the console log will not update when `count` changes. |
| 128 | +``` |
| 129 | + |
| 130 | +Since initialization is a **one-time event**, if a signal is accessed _outside of a tracking scope_, it will not be tracked. |
| 131 | +To track a signal, it must be accessed within the scope of a subscriber. |
| 132 | +Reactive primitives, such as [effects](/concepts/effects), can be used to create subscribers. |
| 133 | + |
| 134 | +```jsx |
| 135 | +const [count, setCount] = createSignal(0); |
| 136 | + |
| 137 | +createEffect(() => { |
| 138 | + console.log("Count:", count()); |
| 139 | +}); |
| 140 | + |
| 141 | +setCount(1); |
| 142 | + |
| 143 | +// Output: Count: 0 |
| 144 | +// Count: 1 |
| 145 | +``` |
| 146 | + |
| 147 | +### Updating the UI |
| 148 | + |
| 149 | +The UI of a Solid application is built using [JSX](/concepts/understanding-jsx). |
| 150 | +JSX creates a tracking scope behind the scenes, which allows signals to be tracked within the return statement of a component. |
| 151 | + |
| 152 | +```jsx |
| 153 | +function Counter() { |
| 154 | + const [count, setCount] = createSignal(0); |
| 155 | + const increment = () => setCount((prev) => prev + 1); |
| 156 | + |
| 157 | + return ( |
| 158 | + <div> |
| 159 | + <span>Count: {count()}</span>{" "} |
| 160 | + {/* ✅ will update when `count()` changes. */} |
| 161 | + <button type="button" onClick={increment}> |
| 162 | + Increment |
| 163 | + </button> |
| 164 | + </div> |
| 165 | + ); |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +Components, much like other functions, will only run _once_. |
| 170 | +This means that if a signal is accessed outside of the return statement, it will run on initialization, but any updates to the signal will not trigger an update. |
| 171 | + |
| 172 | +```jsx |
| 173 | +function Counter() { |
| 174 | + const [count, setCount] = createSignal(0); |
| 175 | + const increment = () => setCount((prev) => prev + 1); |
| 176 | + |
| 177 | + console.log("Count:", count()); // ❌ not tracked - only runs once during initialization. |
| 178 | + |
| 179 | + createEffect(() => { |
| 180 | + console.log(count()); // ✅ will update whenever `count()` changes. |
| 181 | + }); |
| 182 | + |
| 183 | + return ( |
| 184 | + <div> |
| 185 | + <span>Count: {count()}</span>{/* ✅ will update whenever `count()` changes. */} |
| 186 | + <button type="button" onClick={increment}> |
| 187 | + Increment |
| 188 | + </button> |
| 189 | + </div> |
| 190 | + ); |
| 191 | +} |
| 192 | +``` |
| 193 | + |
| 194 | +To learn more about managing state in Solid, visit the [guide on state management](/guides/state-management). |
| 195 | + |
| 196 | +## Synchronous vs. asynchronous |
| 197 | + |
| 198 | +Reactive systems are designed to respond to changes in data. |
| 199 | +These responses can be immediate or delayed, depending on the nature of the system. |
| 200 | +Often, the choice between these two depends on the requirements of the application and the nature of the tasks involved. |
| 201 | + |
| 202 | +### Synchronous reactivity |
| 203 | + |
| 204 | +[Synchronous](https://developer.mozilla.org/en-US/docs/Glossary/Synchronous) reactivity is Solid's default reactivity mode, where a system responds to changes in a direct and linear fashion. |
| 205 | +When a signal changes, any corresponding subscribers are immediately updated in an ordered manner. |
| 206 | + |
| 207 | +With synchronous reactivity, the system is able to respond to changes in a predictable manner. |
| 208 | +This is useful in scenarios where the order of updates is important. |
| 209 | +For example, if a subscriber depends on another signal, it is important that the subscriber is updated after the signal it depends on. |
| 210 | + |
| 211 | +```jsx |
| 212 | +const [count, setCount] = createSignal(0); |
| 213 | +const [double, setDouble] = createSignal(0); |
| 214 | + |
| 215 | +createEffect(() => { |
| 216 | + setDouble(count() * 2); |
| 217 | +}); |
| 218 | +``` |
| 219 | + |
| 220 | +In this example, the `double` signal will always be updated after `count` due to synchronous reactivity. |
| 221 | +This ensures that `double` is always up-to-date with the latest value of `count`. |
| 222 | + |
| 223 | +### Asynchronous reactivity |
| 224 | + |
| 225 | +[Asynchronous](https://developer.mozilla.org/en-US/docs/Glossary/Asynchronous) reactivity is when a system responds to changes in a delayed or non-linear fashion. |
| 226 | +When a signal changes, the corresponding subscribers are not immediately updated. |
| 227 | +Instead, the system waits for a specific event or task to complete before updating the subscribers. |
| 228 | + |
| 229 | +This is important in scenarios where subscribers depend on multiple signals. |
| 230 | +In these cases, updating one signal before another could result in data inconsistency. |
| 231 | +For example, if a subscriber depends on two signals, it is important that the subscriber is updated after both signals have been updated. |
| 232 | +Rather, the system waits for both signals to be updated before updating the subscriber. |
| 233 | + |
| 234 | +**Note:** When asynchronous reactivity is present, it is important to ensure that the system is able to handle the delay in updates. |
| 235 | +[`batch`](/reference/reactive-utilities/batch) can be used to delay an update so the subscriber runs after each signal has been updated. |
| 236 | + |
| 237 | +## Key concepts |
| 238 | + |
| 239 | +- Signals are the core elements of a reactive system. |
| 240 | + They are responsible for storing and managing data. |
| 241 | +- Signals are both readable and writeable because of getters and setters. |
| 242 | +- Subscribers are automated responders that track changes in signals and update the system accordingly. |
| 243 | +- Signals and subscribers work together to ensure that the system is kept up-to-date with the latest data changes. |
| 244 | +- A reactive system is built on the principles of data-driven reactivity. |
| 245 | + This means that the system's reactivity is driven by the data it is built on. |
| 246 | +- Reactive systems can be synchronous or asynchronous. |
| 247 | + |
| 248 | +If you want to dive deeper, visit the [guide on fine-grained reactivity](/advanced-concepts/fine-grained-reactivity). |
0 commit comments