If you are a Frontend developer then you might have heared about Signals. Here is what Signals are, what are their advantages and how you can use them with React.
đź”· What are Signals ?
- A signal is an object that has a value and can be observed for changes. It is similar to a state in React components, but it is not bound to a single component unlike State.
- We can use Signal to manage state in React components.
- We can share data between components directly using signals, eliminating the need for
prop drilling
orContext API
. - When the value of a Signal changes, only the specific DOM element within the component that uses the Signal value is re-rendered, rather than the entire component.
- Whenever the value of a Signal changes, all the components that use that Signal value are re-rendered.
đź”· How to use Signals ?
Preact
is an alternative library which uses same API as React
. But the size of Preact
is 3kb
which is much smaller than React
, which is about 45kb
hence it ships less code to the browser.
Recently, Preact
have announced a new package called @preact/signals-react which is available to install in React
as well and using this package we can use Signals in React and use it for state management.
In your React project you can install the package using the below command.
npm install @preact/signals-react
Once installed now you can import signal()
function from @preact/signals-react
and create new Signal.
đź”· You can access the value of Signal using signal.value
and set the value of the signal using signal.value = newValue
đź”· In contrast to state, Signals can be created outside of components. Updating Signals does not require the use of the setState method, unlike state. The value of a Signal can be updated directly using the signal.value property.
import { signal } from "@preact/signals-react";
// âś… Create a new Signal as shown below
const count = signal(0);
// âś… Create a function to update Signal value
function increaseCounter() {
count.value += 1;
}
Several popular frameworks, including Vue
, Preact
, Solid
, and Qwik
, support Signals. However, there are slight differences in how Signals work and their syntax among these frameworks. Therefore, if you plan to use Signals in any of the mentioned frameworks, it is recommended to refer to the respective framework’s documentation.
However, f you are interested in learning how to use Signals in React, you can continue reading this article.
đź“Ś Benefits of using Signal
Here are some of the benefits of using Signal
over State
.
âś… It does not re-render the entire component when Signal value changes
When Signal
value changes then it does not re-render the entire component unlike State
in React. Instead it only re-renders that specific part of the DOM where the value of signal is being used.
Here is an example of a counter application using State
. When the Increase Counter button is clicked, the State
is updated and the entire component is re-rendered. This can lead to performance issues, especially for large components, as a single state update triggers the re-rendering of the entire component.
// CounterWithState.jsx -- using State
import React, { useState } from "react";
function CounterWithState() {
const [count, setCount] = useState(0);
// âś… The below line gets invoked when State is updated
console.log("CounterWithState Rendered!");
return (
<>
<h4>Inside "CounterWithState" component</h4>
<h3>The count is {count} </h3>
<button onClick={() => setCount(count + 1)}>Increase Counter</button>{" "}
</>
);
}
export default CounterWithState;
Now we will build the same counter application but now using Signal
.
// CounterWithSignal.jsx -- using Signals
import React from "react";
import { signal } from "@preact/signals-react";
// âś… Create a signal
export const count = signal(0);
const CounterWithSignal = () => {
// âś… The below line does not gets invoked then Signal value changes
console.log("CounterWithSignals Rendered!");
return (
<div>
<hr></hr>
<h3>Inside "CounterWithSignals" component</h3>
<h3> Current value count is {count}</h3>
<button onClick={() => count.value++}> Increase Counter</button>
<hr></hr>
</div>
);
};
export default CounterWithSignal;
Now in the Counter application built with Signal
if we click on Increase Button then it does not re-render the entire CounterWithSignal
component, Instead it only updates the specifc DOM element where we are using the Signal
value.
As you can see in the below GIF when Increase Counter is clicked in Counter App made using Signal
, then it is not re-rendering the entire component. Instead it is only re-rendering that specific part of the DOM where the Signal value is being used.
So using Signal
we can avoid unnecessary rerender of the entire component when a small part is being changed.
âś… Using Signal you can directly pass data from one component to another without prop drilling or Context API
Using Signal we can directly pass data across components hence we don’t need to use prop drilling
or Context API
to pass data across different components.
đź”· In the below example we have created a new component as TestChild
and inside it we can directly import the count Signal from the CounterWithSignal
component and hence it removes the need of using prop drilling
and Context API
to pass data across components.
// TestChild.jsx
import React from "react";
// âś… You can directly import the Signal from other component without need to prop drill or Context API
import { count } from "./CounterWithSignal";
function TestChild() {
return (
<>
<h5>Inside the TestChild Component</h5>
<h3>Current value of count is {count}</h3>
<hr></hr>
</>
);
}
export default TestChild;
đź”· And since we are using count
signal value in the TestChild
component hence the TestChild
component will automatically subscribe to the changes of counter
signal. Now whenever the value of counter
signal changes in the CounterWithSignal
component then TestChild
component will also re-render automatically and receive the updated value of counter signal.
Here the entire TestChild component won’t re-render, instead the specific part where we have used the Signal value, only that specific part will be re-rendered.
âś… Updating signal value in parent component does not re-render all the child components
Suppose you have a parent component as App
and inside it you have two siblings components as ComponentA
and ComponentB
. Now if any state gets updated in App
component it will rerender all of its child components. Even if we are not using that state in the child components then also all the child components will still be re-rendered. This becomes a problem specially when you have lot’s of child components being rendered inside a parent component.
But if you use Signal
instead of state
then even if the value of Signal
gets updated in the parent component the child will not re-render until unless the child component uses that signal value. Hence this way we can prevent unnecessary re-render of the child components which is not using state value.
âś… effect() function to track when Signal value changes
Signal provide us a function as effect
which is quite similar to useEffect
hook. But in effect
function we don’t have to pass dependencies array like the useEffect
hook. It’ll automatically detect dependencies being used inside of the effect
function and call effect only when dependencies change.
import React from "react";
import { signal, effect } from "@preact/signals-react";
const count = signal(0);
function Counter() {
// âś… Below effect function runs when Signal value changes
effect(() => {
console.log("ConterWithSignals Rerendered!!");
console.log(count.value);
});
return (
<>
<button
onClick={() => {
count.value++;
}}
>
Increase Counter
</button>
</>
);
}
export default Counter;
In the above code when we click on the Increase Counter button then it changes the value of signal and since we have used the signal value inside of the effect()
function hence it will take the signal value as the dependency and hence the statements written inside of effect()
function gets run.
This was a brief introduction of Signals in React.
⚒️ Advanced usage of Signals
You can refer to the Preact documentation to see some more advanced usage of Signals. Here is the link to the Preact documentation.