reactjssignalspreact

How to compose preact/signals in React for a huge editable datatable


I have a datatable in react, and I'm trying to make it more lightweight so it is usable with larger datasets on weaker devices. I'm experimenting with a few things and I heard good things about preact/signals, but the examples I see are very basic use-cases where I'm very doubtful about the performance gains. I see 2 kinds of examples.

  1. The signal is a simple value that you can directly render, in this case I can see that the optimization is that the component is not even rendered as the signal is not unwrapped in the component, like:
import { signal } from "@preact/signals-react";

const count = signal(0);

function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => count.value++}>click me</button>
    </div>
  );
}
  1. The signal is a large object, it is unwrapped pretty early and the value is passed down as props. This does not seem different to having state in the top component as everything rerenders on change anyway, like:
import { signal } from "@preact/signals";

const todos = signal([
  { text: "Buy groceries" },
  { text: "Walk the dog" },
]);

What I want to achieve is that I have a list of elements with many properties (all have the same type) where I could create a virtualized list with something like react-window. I can "subscribe" to only fragments of the state, so the app is only rerendering what is absolutely necessary.

So the list could be something like this where you can edit certain fields (which are not calculated)

First Name Last Name Full Name Email Phone
Lorine Rhoda Lorine Rhoda ... ...
Jaylynn Heaven Jaylynn Heaven ... ...
Marigold Fox Marigold Fox ... ...

Is it possible to make this list in a way that modifying the last name of a person only rerenders that specific row's last name and full name? (or better yet, does not even rerender the cell, just changes the string)

How would I make my signal "granularly subscribable and modifiable"?


Solution

  • It's quite simple actually: signals can be nested.

    import { signal } from '@preact/signals-react';
    
    const person = signal({
      firstName: signal('John'),
      lastName: signal('Doe'),
    });
    
    function App() {
      console.log('component rerender');
      return (
        <div>
          <p>{person.value.firstName}</p>
          <p>{person.value.lastName}</p>
          <input onInput={(e) => (person.value.firstName.value = e.target.value)} />
        </div>
      );
    }
    

    You can edit the first name using that input and the full component will not rerender; instead, just the text for the firstName will update.

    For your table, you'd do something similar, have a signal wrapping an array in which each record uses signals for its individual properties.