mirror of
https://github.com/Dvorinka/Devour.git
synced 2026-06-03 20:13:03 +00:00
17 lines
15 KiB
JSON
17 lines
15 KiB
JSON
{
|
|
"id": "d1d070f248135d6c6b7fd52b",
|
|
"source": "solid:signals",
|
|
"type": "github-document",
|
|
"title": "state-management",
|
|
"content": "---\ntitle: State management\norder: 2\nuse_cases: \u003e-\n managing app state, component communication, data flow, reactive updates,\n shared state, derived values\ntags:\n - state\n - signals\n - reactivity\n - data-flow\n - memos\n - effects\n - management\nversion: '1.0'\ndescription: \u003e-\n Learn Solid's state management with signals, derived values, memos, and\n effects for reactive data flow and component updates.\n---\n\nState management is the process of handling and manipulating data that affects the behavior and presentation of a web application.\nTo build interactive and dynamic web applications, state management is a critical aspect of development.\nWithin Solid, state management is facilitated through the use of reactive primitives.\n\nThese state management concepts will be shown using a basic counter example:\n\n```jsx\nimport { createSignal } from \"solid-js\";\n\nfunction Counter() {\n\tconst [count, setCount] = createSignal(0);\n\n\tconst increment = () =\u003e {\n\t\tsetCount((prev) =\u003e prev + 1);\n\t};\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\t\u003c/\u003e\n\t);\n}\n```\n\nThere are 3 elements to state management:\n\n1. **State (`count`)**: The _data_ that is used to determine what content to display to the user.\n\n2. **View (`\u003cdiv\u003e{count()}\u003c/div\u003e`)**: The _visual representation_ of the state to the user.\n\n3. **Actions (`increment`)**: Any event that _modifies_ the state.\n\nThese elements work together to create a \"one way data flow\".\nWhen actions modify the state, the view is updated to show the current state to the user.\nOne way data flow simplifies the management of data and user interactions, which provides a more predictable and maintainable application.\n\n## Managing basic state\n\nState is the source of truth for the application, and is used to determine what content to display to the user.\nState is represented by a [signal](/concepts/signals), which is a reactive primitive that manages state and notifies the UI of any changes.\n\nTo create a piece of state, you use the [`createSignal`](/reference/basic-reactivity/create-signal) function and pass in the initial value of the state:\n\n```jsx\nimport { createSignal } from \"solid-js\";\n\nconst [count, setCount] = createSignal(0);\n```\n\nTo access the current value of the state, you call the signal's getter function:\n\n```jsx\nconsole.log(count()); // 0\n```\n\nTo update the state, you use the signal's setter function:\n\n```jsx\nsetCount((prev) =\u003e prev + 1);\n\nconsole.log(count()); // 1\n```\n\nWith signals, you can create and manage state in a simple and straightforward manner.\nThis allows you to focus on the logic of your application, rather than the complexities of state management.\nAdditionally, signals are reactive, which means as long as it is accessed within a [tracking scope](/concepts/intro-to-reactivity#tracking-changes), it will always be up to date.\n\n## Rendering state in the UI\n\nTo achieve a dynamic user interface, the UI must be able to reflect the current state of the data.\nThe UI is the visual representation of the state to the user, and is rendered using JSX.\nJSX provides a tracking scope, which keeps the view in sync with the state.\n\nRevisiting the `Counter` component presented earlier, rendering the current state of `count` is done within the return body using JSX:\n\n```jsx\nreturn (\n\t\u003c\u003e\n\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\u003c/\u003e\n);\n```\n\nTo render the current state of `count`, the JSX expression `{count()}` is used.\nThe curly braces indicate that the expression is a JavaScript expression, and the parentheses indicate that it is a function call.\nThis expression is representative of a getter function for `count` and will retrieve the current state value.\nWhen the state is updated, the UI will be re-rendered to reflect the new state value.\n\nComponents in Solid only run once upon their initialization.\nAfter this initial render, if any changes are made to the state, only the portion of the DOM that is directly associated with the signal change will be updated.\n\nThe ability to update only the relevant portions of the DOM is a key feature of Solid that allows for performant and efficient UI updates.\nThis is known as [fine-grained reactivity](/advanced-concepts/fine-grained-reactivity).\nThrough reducing the re-rendering of entire components or larger DOM segments, UI will remain more efficient and responsive for the user.\n\n## Reacting to changes\n\nWhen the state is updated, any updates are reflected in the UI.\nHowever, there may be times when you want to perform additional actions when the state changes.\n\nFor example, in the `Counter` component, you may want to display the doubled value of `count` to the user.\nThis can be achieved through the use of [effects](/concepts/effects), which are reactive primitives that perform side effects when the state changes:\n\n```jsx\nimport { createSignal, createEffect } from \"solid-js\";\n\nfunction Counter() {\n\tconst [count, setCount] = createSignal(0);\n\tconst [doubleCount, setDoubleCount] = createSignal(0); // Initialize a new state for doubleCount\n\n\tconst increment = () =\u003e {\n\t\tsetCount((prev) =\u003e prev + 1);\n\t};\n\n\tcreateEffect(() =\u003e {\n\t\tsetDoubleCount(count() * 2); // Update doubleCount whenever count changes\n\t});\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e // Display the doubled count\n\t\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\t\u003c/\u003e\n\t);\n}\n```\n\nThe [`createEffect`](/reference/basic-reactivity/create-effect) function sets up a function to perform side effects whenever the state is modified.\nHere, a side-effect refers to operations or updates that affect state outside of the local environment - like modifying a global variable or updating the DOM - triggered by those state changes.\n\nIn the `Counter` component, a `createEffect` function can be used to update the `doubleCount` state whenever the `count` state changes.\nThis keeps the `doubleCount` state in sync with the `count` state, and allows the UI to display the doubled value of `count` to the user.\n\nView this example of [`doubleCount` in a `createEffect` in the Solid Playground example](https://playground.solidjs.com/anonymous/b05dddaa-e62a-4c56-b745-5704f3a40194).\n\n```html tab title=\"First render\"\nCurrent count: 0 Doubled count: 0\n```\n\n```html tab title=\"After increment\"\nCurrent count: 1 Doubled count: 2\n```\n\n## Derived state\n\nWhen you want to calculate new state values based on existing state values, you can use derived state.\nThis is a useful pattern when you want to display a transformation of a state value to the user, but do not want to modify the original state value or create a new state value.\n\nDerived values can be created using a signal within a function, which can be referred to as a [derived signal](/concepts/derived-values/derived-signals).\n\nThis approach can be used to simplify the `doubleCount` example above, where the additional signal and effect can be replaced with a derived signal:\n\n```jsx del={5, 11-13} ins={15}\nimport { createSignal } from \"solid-js\"\n\nfunction Counter() {\n\tconst [count, setCount] = createSignal(0);\n\tconst [doubleCount, setDoubleCount] = createSignal(0);\n\n\tconst increment = () =\u003e {\n\t\tsetCount((prev) =\u003e prev + 1);\n\t};\n\n\tcreateEffect(() =\u003e {\n\t\tsetDoubleCount(count() * 2); // Update doubleCount whenever count changes\n\t});\n\n\tconst doubleCount = () =\u003e count() * 2\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\t\u003c/\u003e\n\t);\n}\n```\n\nWhile this approach works for simple use cases, if `doubleCount` is used several times within a component or contains a computationally expensive calculation, it can lead to performance issues.\n\nThe derived signal would be re-evaluated not just each time `count` is changed, but also for each use of `doubleCount()`.\n\n```jsx del={10} ins={11-14, 20-21}\nimport { createSignal } from \"solid-js\"\n\nfunction Counter() {\n\tconst [count, setCount] = createSignal(0)\n\n\tconst increment = () =\u003e {\n\t\tsetCount(count() + 1)\n\t}\n\n\tconst doubleCount = () =\u003e count() * 2\n\tconst doubleCount = () =\u003e {\n\t\tconsole.log('doubleCount called')\n\t\treturn count() * 2\n\t}\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\t\u003c/\u003e\n\t)\n}\n```\n\n```shellsession title=\"Console output\"\ndoubleCount called\ndoubleCount called\ndoubleCount called\n```\n\nFor cases like this, you can use [Memos](/concepts/derived-values/memos) to store the value of `doubleCount`, which are also referred to as a memoized or cached value.\nWhen using a memo, the calculation will only run **once** when the value of `count` changes and can be accessed multiple times without re-evaluating for each additional use.\n\nUsing the [`createMemo`](/reference/basic-reactivity/create-memo) function, you can create a memoized value:\n\n```jsx ins={15-18, 26-28} ins=\", createMemo\"\nimport { createSignal, createMemo } from \"solid-js\"\n\nfunction Counter() {\n\tconst [count, setCount] = createSignal(0)\n\n\tconst increment = () =\u003e {\n\t\tsetCount((prev) =\u003e prev + 1);\n\t};\n\n\tconst doubleCount = () =\u003e {\n\t\tconsole.log('doubleCount called')\n\t\treturn count() * 2\n\t}\n\n\tconst doubleCountMemo = createMemo(() =\u003e {\n\t\tconsole.log('doubleCountMemo called')\n\t\treturn count() * 2\n\t})\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {count()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCount()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCountMemo()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCountMemo()}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {doubleCountMemo()}\u003c/div\u003e\n\t\t\t\u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n\t\t\u003c/\u003e\n\t);\n}\n```\n\n```shellsession title=\"Console output\"\ndoubleCountMemo called\ndoubleCount called\ndoubleCount called\ndoubleCount called\n```\n\nWhile accessed multiple times, the `doubleCountMemo` will only re-evaluate and log once.\nThis is different from the derived signal, `doubleCount`, which is re-evaluated for each time it is accessed.\n\nView a similar [example comparing a derived signal and a memo in the Solid Playground](https://playground.solidjs.com/anonymous/288736aa-d5ba-45f7-a01f-1ac3dcb1b479).\n\n## Lifting state\n\nWhen you want to share state between components, you can lift state up to a common ancestor component.\nWhile state is not tied to components, you may want to link multiple components together in order to access and manipulate the same piece of state.\nThis can keep things synchronized across the [component tree](/concepts/components/basics#component-trees) and allow for more predictable state management.\n\nFor example, in the `Counter` component, you may want to display the doubled value of `count` to the user through a separate component:\n\n```jsx\nimport { createSignal, createEffect, createMemo } from \"solid-js\";\n\nfunction App() {\n\tconst [count, setCount] = createSignal(0);\n\tconst [doubleCount, setDoubleCount] = createSignal(0);\n\tconst squaredCount = createMemo(() =\u003e count() * count());\n\n\tcreateEffect(() =\u003e {\n\t\tsetDoubleCount(count() * 2);\n\t});\n\n\treturn (\n\t\t\u003c\u003e\n\t\t\t\u003cCounter count={count()} setCount={setCount} /\u003e\n\t\t\t\u003cDisplayCounts\n\t\t\t\tcount={count()}\n\t\t\t\tdoubleCount={doubleCount()}\n\t\t\t\tsquaredCount={squaredCount()}\n\t\t\t/\u003e\n\t\t\u003c/\u003e\n\t);\n}\n\nfunction Counter(props) {\n\tconst increment = () =\u003e {\n\t\tprops.setCount((prev) =\u003e prev + 1);\n\t};\n\n\treturn \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e;\n}\n\nfunction DisplayCounts(props) {\n\treturn (\n\t\t\u003cdiv\u003e\n\t\t\t\u003cdiv\u003eCurrent count: {props.count}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eDoubled count: {props.doubleCount}\u003c/div\u003e\n\t\t\t\u003cdiv\u003eSquared count: {props.squaredCount}\u003c/div\u003e\n\t\t\u003c/div\u003e\n\t);\n}\n\nexport default App;\n```\n\nTo share the `count` state between the `Counter` and `DisplayCounts` components, you can lift the state up to the `App` component.\nThis allows the `Counter` and `DisplayCounts` functions to access the same piece of state, but also allows the `Counter` component to update the state through the `setCount` setter function.\n\nWhen sharing state between components, you can access the state through [`props`](/concepts/components/props).\nProps values that are passed down from the parent component are read-only, which means they cannot be directly modified by the child component.\nHowever, you can pass down setter functions from the parent component to allow the child component to indirectly modify the parent's state.\n\n:::note\nTo encourage one-way data flow, props are passed as read-only or immutable values from the parent to child components.\n\nThere are [specific utility functions for props](/concepts/components/props), however, that offer methods to modify props values.\n\n:::\n\n## Managing complex state\n\nAs applications grow in size and complexity, lifting state can become difficult to manage.\nTo avoid the concept of prop drilling, which is the process of passing props through multiple components, Solid offers [stores](/concepts/stores) to manage state in a more scalable and maintainable manner.\n\nTo learn more about managing complex state, navigate to the [complex state management page](/guides/complex-state-management).",
|
|
"url": "https://github.com/solidjs/solid-docs/blob/HEAD/src/routes/guides/state-management.mdx",
|
|
"metadata": {
|
|
"path": "src/routes/guides/state-management.mdx",
|
|
"repo": "solidjs/solid-docs",
|
|
"repo_url": "https://github.com/solidjs/solid-docs.git",
|
|
"size": 13217,
|
|
"source_type": "github"
|
|
},
|
|
"hash": "f537493a0edcb38a7638e95d547bee56d961145a11f02992b54407e5f86e691a",
|
|
"timestamp": "2026-02-23T11:43:00.187855372+01:00"
|
|
} |