DEV Community

React.js state management using signals

Rahul Sharma on September 21, 2022

A signal is an object that has a value and can be observed for changes. It is similar to a state, but it is not bound to a component. It can be use...
Collapse
 
elpddev profile image
Eyal Lapid •

This is only for preact? Because of the custom jsx renderer that can hook into singals?

Collapse
 
elpddev profile image
Eyal Lapid •

Ok, I have just found out there is a specfic version for React also from the same authors.

npmjs.com/package/@preact/signals-...

Collapse
 
marcoselopez profile image
Marcos Emmanuel López • • Edited

Hi Rahul!
Thank you so much for this guide. I have a little problem, well, I don't know if it's a problem but it seems odd.
I was messing with creating a signal that contained an object like this:
const counters = signal({
counter1: 0,
counter2: 0,
})

and in my component I've declared an effect to see that value change, like this:
effect(() => console.log(count.value))

And then I want to update one of those values like this:
const increment = (val) => {
counters.value = {
...counters.value,
counter1: count.value.counter1 += val
}
}

But what I notice in the console is that the value of the signal prints more and more everytime it changes, for example, when I press increment, it will print the object 1 time, if I press it again, it prints the object 2 times, If I press it againt, it prints the object 3 times, and so on.

Do you know why this happens?

EDIT:
Ok, I've just found out that if instead of using effect I use batch, the console log fires only 1 time per change:
batch(() => {
console.log(count.value)
})

But I still don't understand why that happens?

Collapse
 
danvladandreev profile image
danVladAndreev • • Edited

This is probably because you effect isn't destroyed after the re-render.
You can try this:

import {useSignalEffect} from '@preact/signals-react';
...
useSignalEffect(() => console.log(count.value));

This should create the effect only once.

Collapse
 
duoc95 profile image
Duoc95 •

hi Rahul, can you use object with signal. thanks in advance.

Collapse
 
rsmelo92 profile image
Rafael Melo •

yes you can, check this section here on docs

Combining multiple updates into one
preactjs.com/guide/v10/signals#usa...

Collapse
 
duoc95 profile image
Duoc95 • • Edited
import React from 'react'
import {  signal } from '@preact/signals-react'

function Todos(props) { 
    const todos = signal([
        { name: '123'},
    ])

    const addToDo = () => {
        todos.value = [...todos.value, { name: '123123'}]
    }

    return (
    <div>
        <button onClick={() => {
            addToDo();
        }}>add to do</button>
        {
            todos.value.map(({ name }, index) => (<div key={index} style={{
                fontWeight: 'bold'
            }}>{name}</div>))
        }
    </div>
  )
}

export default Todos

Enter fullscreen mode Exit fullscreen mode

i have tried, but my UI didn't update after click add new Todo.

Thread Thread
 
karrotkardiachain profile image
Dang Khoa •

I found that todos value will be create new when you change value. Just move it outside a Todos component to make it works properly.

const todos = signal([
        { name: '123'},
    ]);
function Todos(props)
...
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
duoc95 profile image
Duoc95 •

thank you, Khoa, i will try it.

Thread Thread
 
duoc95 profile image
Duoc95 •

i've tried but not working like you said, could you plz create the sandbox this case? thanks in advance.

Collapse
 
indrajitbnikam profile image
Indrajeet Nikam •

Hi Rahul, Great article.

I have a question though, Is there any way to restrict access to signals so that signals are not-misused by team members in the future (let's say after 2-3 years).

Because the way I understand it, anybody can change it right? It's usually not an issue for senior folks in team but junior devs tend to misuse such highly flexible mechanism.

How can I prevent that? that's my biggest concern for using signals in our massive codebase at company.

Collapse
 
devsmitra profile image
Rahul Sharma •

I don't think we can restrict anyone from using it, because it's the public library.
One thing you can do is add an eslint rule for such imports. I usually do that to forcefully adopt "Tree Shaking" from any library.

FYR: mui.com/material-ui/guides/minimiz...

{
  "rules": {
    "no-restricted-imports": [
      "error",
      {
        "patterns": ["@mui/*/*/*"]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
indrajitbnikam profile image
Indrajeet Nikam •

This is certainly a way to enforce imports but my main concern was slightly different.

I want to avoid concerns like somebody changing the state of signal from outside the component where it is not supposed to be accessed. I searched a lot for the solution,

I came to the conclusion that, only sensible way of doing that is through this pattern/recipe

Image description

You can read more here, preactjs.com/guide/v10/signals#man...

I tested it in code-sandbox and it works as expected while creating different signal instances for different components. you can play with it here,
codesandbox.io/s/react-16-9-0-fork...

Thread Thread
 
devsmitra profile image
Rahul Sharma •

I got your point.

We can create small stores (like user, todo, etc) using the signal. All the actions related to user/todo should use that. Svelte and Solid.js follow the same pattern for creating custom stores.

Collapse
 
rsmelo92 profile image
Rafael Melo •

isnt this 1:1 with the preact docs?
preactjs.com/guide/v10/signals#usa...

Collapse
 
pianoboy profile image
pianoboy •

You mast find this,I think it is better!:npmjs.com/package/react-use-signal

Collapse
 
haraso profile image
Péter Hragyil •

If you would like to use signals with react try my lib. It made for react.

npmjs.com/package/react-signaler