Skip to content

Commit

Permalink
improving the derived state section to fix #489 (#539)
Browse files Browse the repository at this point in the history
Co-authored-by: Sarah <gerrardsarah@gmail.com>
Co-authored-by: Atila Fassina <atila@fassina.eu>
  • Loading branch information
3 people authored Jun 19, 2024
1 parent 86bdaff commit 1ec1902
Showing 1 changed file with 82 additions and 31 deletions.
113 changes: 82 additions & 31 deletions src/routes/guides/state-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function Counter() {
};

createEffect(() => {
setDoubleCount(count() * 2); // Update doubleCount whenever count changes
setDoubleCount((prev) => prev * 2); // Update doubleCount whenever count changes
});

return (
Expand All @@ -140,6 +140,8 @@ Here, a side-effect refers to operations or updates that affect state outside of
In the `Counter` component, a `createEffect` function can be used to update the `doubleCount` state whenever the `count` state changes.
This keeps the `doubleCount` state in sync with the `count` state, and allows the UI to display the doubled value of `count` to the user.

View this example of [`doubleCount` in a `createEffect` in the Solid Playground example](https://playground.solidjs.com/anonymous/b05dddaa-e62a-4c56-b745-5704f3a40194).

<TabsCodeBlocks>
<div id="First render">
```html title="Counter.tsx" Current count: 0 Doubled count: 0 ```
Expand All @@ -153,31 +155,29 @@ This keeps the `doubleCount` state in sync with the `count` state, and allows th
## Derived state

When you want to calculate new state values based on existing state values, you can use derived state.
Derived state is state that is calculated from other state values.
This 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.

Derived values can be created through using a signal within a function, which can be referred to as a [derived signal](/concepts/derived-values/derived-signals):
Derived values can be created using a signal within a function, which can be referred to as a [derived signal](/concepts/derived-values/derived-signals).

```jsx
import { createSignal } from "solid-js";
This approach can be used to simplify the `doubleCount` example above, where the additional signal and effect can be replaced with a derived signal:

```jsx del={5, 11-13} ins={15}
import { createSignal } from "solid-js"

function Counter() {
const [count, setCount] = createSignal(0);
const [doubleCount, setDoubleCount] = createSignal(0);

// Derived signal
const squaredCount = () => {
return count() * count();
};

const increment = () => {
setCount((prev) => prev + 1);
};

createEffect(() => {
setDoubleCount(count() * 2); // Update doubleCount whenever count changes
setDoubleCount((prev) => prev * 2); // Update doubleCount whenever count changes
});

const doubleCount = () => count() * 2

return (
<>
<div>Current count: {count()}</div>
Expand All @@ -188,44 +188,95 @@ function Counter() {
}
```

While this approach works, it can be inefficient as the `doubleCount` signal will be re-evaluated on every render.
When a function is computationally expensive, or used multiple times within a component, this can lead to performance issues.
To avoid this, Solid introduced [Memos](/concepts/derived-values/memos):
While 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.

```jsx
import { createSignal, createEffect, createMemo } from "solid-js";
The derived signal would be re-evaluated not just each time `count` is changed, but also for each use of `doubleCount()`.

```jsx del={10} ins={11-14, 20-21}
import { createSignal } from "solid-js"

function Counter() {
const [count, setCount] = createSignal(0);
const [doubleCount, setDoubleCount] = createSignal(0);
const [count, setCount] = createSignal(0)

// Memo
const squaredCount = createMemo(() => count() * count());
const increment = () => {
setCount(count() + 1)
}

const doubleCount = () => count() * 2
const doubleCount = () => {
console.log('doubleCount called')
return count() * 2
}

return (
<>
<div>Current count: {count()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Doubled count: {doubleCount()}</div>
<button onClick={increment}>Increment</button>
</>
)
}
```

```shellsession title="Console output"
doubleCount called
doubleCount called
doubleCount called
```

For 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.
When 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.

Using the [`createMemo`](/reference/basic-reactivity/create-memo) function, you can create a memoized value:

```jsx ins={15-18, 26-28} ins=", createMemo"
import { createSignal, createMemo } from "solid-js"

function Counter() {
const [count, setCount] = createSignal(0)

const increment = () => {
setCount((prev) => prev + 1);
};

const doubleCount = () => {
return count() * 2;
};
console.log('doubleCount called')
return count() * 2
}

const doubleCountMemo = createMemo(() => {
console.log('doubleCountMemo called')
return count() * 2
})

return (
<>
<div>Current count: {count()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Doubled count: {doubleCountMemo()}</div>
<div>Doubled count: {doubleCountMemo()}</div>
<div>Doubled count: {doubleCountMemo()}</div>
<button onClick={increment}>Increment</button>
<div>
<div>Current count: {count()}</div>
<div>Doubled count: {doubleCount()}</div>
<div>Squared count: {squaredCount()}</div>
</div>
</>
);
}
```

Using the [`createMemo`](/reference/basic-reactivity/create-memo) function, you can create a memoized value that is only re-evaluated when its dependencies change.
This means that the value of `squaredCount` will only be re-evaluated when the value of `count` changes.
This is a more efficient approach to calculating derived state, as it only re-evaluates the memoized value when necessary.
```shellsession title="Console output"
doubleCountMemo called
doubleCount called
doubleCount called
doubleCount called
```

While accessed multiple times, the `doubleCountMemo` will only re-evaluate and log once.
This is different from the derived signal, `doubleCount`, which is re-evaluated for each time it is accessed.

View a similar [example comparing a derived signal and a memo in the Solid Playground](https://playground.solidjs.com/anonymous/288736aa-d5ba-45f7-a01f-1ac3dcb1b479).

## Lifting state

Expand All @@ -244,7 +295,7 @@ function App() {
const squaredCount = createMemo(() => count() * count());

createEffect(() => {
setDoubleCount(count() * 2);
setDoubleCount((prev) => prev * 2);
});

return (
Expand Down

0 comments on commit 1ec1902

Please sign in to comment.