Why React Should Not Adopt Signals

Rate this content
Bookmark

Knockout had it first, Vue was always hiding it, Solid made it popular, and now Svelte and Angular are also integrating it: reactivity based on signals. If all these other frameworks lean on this special primitive, should React follow suit as well?

In this talk, we will examine the less-discussed downsides of signals and explain why I believe React should stick to its current approach of plain values and functions.

This talk has been presented at React Advanced 2024, check out the latest edition of this React Conference.

FAQ

React assumes all values can change, requiring explicit dependency specifications and memorization within hooks and components, whereas signal-based frameworks may require deciding which values are signals and which are not.

Using signals in React could complicate the component execution model, as components may not rerun entirely with every change, leading to complexities in handling component updates.

Mixing signals and plain values can lead to complications where some properties can change while others cannot, requiring developers to explicitly manage which values should be signals and which should remain static.

Signals can improve rendering efficiency by ensuring only the components that read the signal are updated when the signal changes, reducing unnecessary rendering of unaffected components.

Single execution components imply that the outer component function runs once to establish effects and signals, but specific parts within the component may update independently based on signal changes.

Adopting signals in React could lead to automatic dependency tracking for effects and memos, potentially resulting in more efficient component rendering by only updating components that consume the signal.

Using signals in custom hooks could require developers to specify which arguments are signals and which are plain values, potentially leading to frequent refactoring as the requirements change.

React's assumption allows developers to work with plain values and ensures that any changes in properties will update the component, leading to more robust and less frequently updated code.

For real-world experiences with signal-based projects, you can contact the speaker via Twitter, email, or Discord to discuss performance issues or general interest in signals.

A signal is a static container created with a function or constructor that has two main functions: one for setting a value and another for retrieving it. This allows automatic tracking of dependencies for effects or memos in frameworks like Solid, Angular, and Vue.js.

Andreas Roth
Andreas Roth
10 min
28 Oct, 2024

Comments

Sign in or register to post your comment.
Video Summary and Transcription
Today I want to talk about why React should not adopt signals. By adopting signals in React, we can automatically track dependencies for effects and memos, leading to more efficient component rendering. Accessing specific parts of the JSX that read signals allows for fine-grained reactivity across component boundaries. Adopting signals in React requires migrating to single execution components, which only update specific parts of the application. This can become complex when dealing with components that read from different signals. In contrast, React assumes everything can change and enforces this assumption through linters and compilers, leading to more robust code and easier updates. If you're interested in signals in React or need performance improvements, let's chat!

1. React and signals

Short description:

Today I want to talk about why React should not adopt signals. A signal is a static container with functions to set and retrieve values. Different frameworks use signals under the hood with varying syntax. By adopting signals in React, we can automatically track dependencies for effects and memos, leading to more efficient component rendering.

Hello everyone from Dresden in Germany. I hope you're having an awesome conference so far. I only have 10 minutes, so let's jump right into the topic.

Today I want to talk about why, in my opinion, React should not adopt signals. To get into this, we first have to discuss the elephant in the room, the signal. What exactly is a signal? As you can see here, a signal is created with a function or a constructor with a new signal or whatever. You get a container back, and this container has usually two functions, one for setting the value and one for retrieving the value. That means that it's a static container that you define once and then you can continue reading and writing values from this one static container. You always have a function for writing and for reading the value, whether that's a direct function call or if it's just accessing a property that has a getter function behind the scenes. Of course, the syntax varies between frameworks. Frameworks like Solid, Angular or Vue.js all use something like signals under the hood with varying syntax. In the end, it's always the same, a static container with two functions, one to set and one to get to retrieve the value. If that's a set of property or a getter, it doesn't matter in the end, it's function calls.

Now, the question is why should we want to adopt signals in something like React? We already have states. The first benefit is that we would be able to automatically track dependencies for effects or for memos. Here, as you can see, this is the Solid.js syntax, but it looks very similar to React, so we can just take a look at it. You can see that we are creating the signal in the first line and then we are using an effect to always lock the number whenever the number changes. Additionally, we have this double number where we have a memo, where you just access the number, you call the function to retrieve the value and multiply it by two. By calling the function, the dependencies can be registered automatically. Whenever the number changes, it's totally clear that the effect needs to rerun and our memo needs to rerun. This works automatically without us having to specify the dependencies as in React currently. This can also lead to more efficient component rendering. If you take a look at this example, we have a top component, a mid component and the bottom component. Top renders mid, renders bottom. As you can see at the top, we are defining and creating the signal, a static container. We are passing it to the mid component and this just takes this container and forwards it to the bottom component, where the value is used. As you can see in the mid component, the signal is never read. It's just forwarded to someone else. Now if you could imagine a future where the framework would just rerender the components where the signal is actually read, we don't need stuff like React.Memo or a shouldComponentUpdate or something like that. We could just always update those components that read a signal only if the signal changes.

2. Reactivity and component boundaries

Short description:

This would not change our mental model, but could track component updates without rendering from top to bottom. Accessing specific parts of the JSX that read signals allows for fine-grained reactivity across component boundaries.

This would not really change anything about our mental model, because it still could rerun the whole component, but it could automatically track which component needs updates without having to render all the way from the top to the bottom.

If we take a look at this last example, the bottom component, and maybe if we make this a bit more complicated, then we can see an even more interesting fact. Here we create another signal and we are logging the signal and then we are reading the props, the number in the JSX. But we are only accessing the signal in this small part of the JSX, in this curly braces. It should also be possible to only update this specific part in the Rome, because nobody else in the whole application is even reading the signal. This then leads to the holy grail, fine-grained reactivity across component boundaries.

Whenever we update a signal, only those specific parts that read the signal, doesn't matter if it's an effect, a memo or a part in the JSX, only those components that really consume the signal should get updated whenever the signal changes.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

SolidJS: Reactivity Unchained
JSNation 2022JSNation 2022
20 min
SolidJS: Reactivity Unchained
Solid.js is a declarative JavaScript library for building user interfaces that addresses performance optimization. It introduces fine-grained reactivity and avoids using a virtual DOM. The Talk explores rethinking performance and reactivity in web applications, understanding reactivity and primitives, and creating DOM elements and using JSX in Solid.js. It also covers rendering components, sharing state, and the advantages of fine-grained rendering and the reactive approach in Solid.js.
5 Years of Building React Table
React Summit 2022React Summit 2022
24 min
5 Years of Building React Table
Top Content
React Table is a popular table library that started with HTML5 tables and transitioned to React. It faced challenges with integration and user requests, leading to the development of React Table. The introduction of the Headless UI pattern and TypeScript support improved the library's capabilities and quality. Generics and TypeScript played a significant role in reducing the code size and improving development. React Table is now going framework agnostic and partnering with AG Grid.
Modern State Management with Vue 3
Vue.js London Live 2021Vue.js London Live 2021
22 min
Modern State Management with Vue 3
Top Content
Vanessa introduces Vue Free and discusses the benefits of using the Composition API. The order of execution and grouping logical units using the Composition API is explained. The Composition API is used for state management and refactoring components. The speaker shares their initial experience with state management using Vuex. Composables are explored as an alternative for state management in Vue 3.
Taking Vue.js to the Backend
Vue.js London Live 2021Vue.js London Live 2021
23 min
Taking Vue.js to the Backend
This talk explores using Vue.js in the backend, specifically focusing on Vue 3 Reactivity. It discusses how Vue 3 Reactivity leverages ES6 proxies to update changes and intercept hooks. The talk also covers implementing Vue.js backend with live demos, showcasing the modification of proxies and the use of reactive functions. It demonstrates the creation of a reactive array and the implementation of join, leave, and message functionalities. The talk concludes by mentioning the possibility of using computed properties and inviting further questions.
Fine-Grained Reactivity Without Any Compiler
React Day Berlin 2024React Day Berlin 2024
29 min
Fine-Grained Reactivity Without Any Compiler
My name is Nicolas, and today, I'm going to talk about Fine-Grain Reactivity without the compiler this time. React is reactive, but it re-renders unnecessary components. Fine-grained reactivity is achieved by updating specific elements instead of whole components. Pigment encountered reactivity problems while building real-time financial data boards. They created their own reactive system on top of React to achieve fine-grained reactivity. They used custom hooks like useShell, useComputed, and useWatch to integrate React with their reactive system. They also considered using native JavaScript for optimization but chose to preserve React. They use external libraries like Zestand for flexibility, and maintaining reactivity during React updates has not been a problem.

Workshops on related topic

Build a Universal Reactive Data Library with Starbeam
JSNation 2023JSNation 2023
66 min
Build a Universal Reactive Data Library with Starbeam
WorkshopFree
Yehuda Katz
Yehuda Katz
This session will focus on Starbeam's universal building blocks. We'll use Starbeam to build a data library that works in multiple frameworks.We'll write a library that caches and updates data, and supports relationships, sorting and filtering.Rather than fetching data directly, it will work with asynchronously fetched data, including data fetched after initial render. Data fetched and updated through web sockets will also work well.All of these features will be reactive, of course.Imagine you filter your data by its title, and then you update the title of a record to match the filter: any output relying on the filtered data will update to reflect the updated filter.In 90 minutes, you'll build an awesome reactive data library and learn a powerful new tool for building reactive systems. The best part: the library works in any framework, even though you don't think about (or depend on) any framework when you built it.
Table of contents- Storing a Fetched Record in a Cell- Storing multiple records in a reactive Map- Reactive iteration is normal iteration- Reactive filtering is normal filtering- Fetching more records and updating the Map- Reactive sorting is normal sorting (is this getting a bit repetitive?)- Modelling cache invalidation as data- Bonus: reactive relationships