signalspreact

The entire component is being re-rendered when using signals


Why is my entire component being re-rendered, even though I'm using the new "signals" library? What am I doing wrong? Only the button is supposed to be re-rendered, not the entire component.

NOTE: I used the Preact Developer Tools to test the component with the option "Highlight updates" activated. This shows the parts of the component that is being re-rendered.

Here's the code:

import { useSignal } from "@preact/signals";

export function Home() {
  const toggle = useSignal(false);

  return (
    <div>
      <button onClick={() => (toggle.value = !toggle.value)}>
        {toggle.value ? "yes" : "no"}
      </button>
      <p>This is re-rendered every time 'toggle.value' changes.</p>
    </div>
  );
}

Solution

  • To take advantage of the rendering optimizations you need to use the signal directly in your component; unwrapping with .value disables this. Docs

    If you want to display different text based upon the signal value, your best bet would be to add a computed in:

    import { useSignal, useComputed } from "@preact/signals";
    
    export function Home() {
      const toggle = useSignal(false);
      const toggled = useComputed(() => (toggle.value ? "yes" : "no"));
    
      return (
        <div>
          <button onClick={() => (toggle.value = !toggle.value)}>
            {toggled}
          </button>
          <p>This is re-rendered every time 'toggle.value' changes.</p>
        </div>
      );
    }
    

    Note: it's generally better to use the non-hook functions (signal and computed vs useSignal and useComputed) if at all possible.