React with Signals

Signals are another way of managing application state.

You might ask, “well great but we already have hooks like useState so what’s the point?”

In answer to the above perfectly valid question, Signals work on a in a more granular way – let’s compare to useState. If we create a simple little (and pretty standard) counter component we can immediately see the differences.

Create yourself a React application, I’m using yarn but use your preferred package manager.

yarn create react-app react-with-signals --template typescript

Add the signal package using

yarn add @preact/signals-react

Now we’ll create a folder named components and add a file named CounterState.tsx which looks like this

import { useState } from "react";

export const CounterState = () => {
  const [count, setCount] = useState(0);

  console.log("Render CounterState");

  return (
    <div>
      <div>Current Value {count}</div>
      <div>
        <button onClick={() => setCount(count - 1)}>Decrement</button>
        <button onClick={() => setCount(count + 1)}>Increment</button>
      </div>
    </div>
  );
}

It’s not very pretty but it’s good enough for this demonstration.

Finally create a new file in the components folder named CounterSignals.tsx which should look like this

import React from 'react';
import { useSignal } from "@preact/signals-react";

export const CounterSignals = () => {
  const count = useSignal(0);

  console.log("Render CounterSignals");

  return (
    <div>
      <div>Current Value {count}</div>
      <div>
        <button onClick={() => count.value--}>Decrement</button>
        <button onClick={() => count.value++}>Increment</button>
      </div>
    </div>
  );
}

As you can see, the Signals code creates a Signals object instead of the destructuring way used with useState and the Signals object will not change, but when we change the value, the value within the object changes but does not re-rendering the entire component each time.

Let’s see this by watching the console output in our preferred browser, so just change App.tsx to look like this

import React from ‘react’;
import { CounterState } from ‘./components/CounterState’;
import { CounterSignals } from ‘./components/CounterSignals’;

function App() {
return (


);
}

export default App;
[/em]

Start the application. You might wish to disable React.StrictMode in the index.tsx as this will double up the console output whilst in DEV mode.

Click the Increment and Decrement buttons and you’ll see our useState implementation renders on each click whereas the counter changes for the Signals version but no re-rendering happens.

Code

Code for this example using Signals can be found on github.