natserract / react-hook-signal

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

react hook signal, seamless way to integrate React with TC39 Signal Proposal

Commitizen friendly codecov Node.js CI

React-Hook-Signal is a tiny library, less than 1kb. It helps you integrate Signal with your React components easily.

Installation

npm install react-hook-signal signal-polyfill

What are Signals?

The TC39 Proposal for Signals in JavaScript aims to establish a mechanism for components to communicate effectively. This proposal includes a polyfill for prototyping purposes.

Understanding Signals

Once adopted, JavaScript will have a native signaling system, referred to as Signal throughout this guide.

How signal can efficiently re-render react component

Solving rerendering problem in react using signal

In React components, re-rendering starts at the component's beginning and extends to the end of the JSX element. Signal usage allows precise re-rendering, boosting performance and simplifying development without the need for memoization or useCallback functions.

Why choose React-Hook-Signal?

  • It's straightforward: Just React and Native Signal, no extra babel plugin needed.
  • Enjoy full TypeScript support for an improved Developer Experience.
  • Flexibility is key: Opt-in for integration in your React project, seamlessly blending state and signal.
  • It's incredibly lightweight, clocking in at less than 1kb

Steps to Integrate Signals with React

STEP 1: Rendering Signal Values:

  • Utilize notifiable components provided by react-hook-signal.
  • notifiable components accept standard HTML attributes, Signal, and Lambda for detecting dependency changes.

Example:

// Global.tsx
import {Signal} from "signal-polyfill";
import {JSXAttribute} from "react-hook-signal";

export const count = new Signal.State(0)
export const renderCount = new Signal.Computed(() => {
    return <div>Count {count.get()}</div>
})

The fastest way to integrate these Signals is to use the notifiable components.

import {count,renderCount} from "./GlobalSignals.tsx";
// here we are importing react-hook-signal:notifiable
import {notifiable} from "react-hook-signal";


export function App() {
    return <>
    
        {/* When user click button it will update the state */}
        <button onClick={() => count.set(count.get() + 1)}>Click here</button>
    
        {/* Following line will get auto update when user click button*/}
        <notifiable.div>{renderCount}</notifiable.div>
    </>
}

notifiable component attributes is not only capable of accepting the Signal type but also can receive Lambda

Lambda is a callback that's able to listen for changes in the signals it depends on.

import {count} from "./GlobalSignals.tsx";
import {notifiable} from "react-hook-signal";


export function App() {
    return <>
    
        {/* When user click button it will update the state */}
        <button onClick={() => count.set(count.get() + 1)}>Click here</button>
    
        {/* Following line will get auto update when user click button*/}
        <notifiable.div>{() => {
            return <div>Count {count.get()}</div>
        }}</notifiable.div>
    </>
}

STEP 2: Observing Signal Changes

  • Use the useSignalEffect hook to listen for changes in Signal.
  • This hook accepts a callback that reads the final signal value and can optionally return a cleanup function.

Important Note:

  • useSignalEffect doesn't automatically re-render the component. Use React.useState to trigger a re-render.

Example:

import {count} from "./GlobalSignals.tsx";
import {useSignalEffect} from "react-hook-signal";
import {useState} from "react";

export function App() {
    const [countState,setCountState] = useState(count.get())
    
    useSignalEffect(() => {
        // Here, within the useSignalEffect hook, I can listen for updates on any Signal.State or Signal.Computed
        setCountState(count.get())
    })
    return <div style={{display:'flex',flexDirection:'column',alignItems:'center'}}>
        {/* When user click button it will update the state */}
        <button onClick={() => count.set(count.get() + 1)}>Click here</button>

        {/* Following line will be updated because of countState updated*/}
        <div>{countState}</div>
    </div>
}

STEP 3: Creating Signals in React Components:

useSignal is a hook used to generate Signal that are tied to the lifespan of a component. It returns two types of Signals : Signal.State and Signal.Computed. Signal.State is a signal whose value can be MODIFIED after it is initially created. On the other hand, Signal.Computed represents a computed signal whose value cannot be modified directly, as it is the result of a computation.

  • To create a Signal.State, simply provide a constant value as a parameter when calling useSignal.
  • To create a Signal.Computed,simply provide a Lambda that returns the result of a dynamic computation.

Example :

import {notifiable, useSignal} from "react-hook-signal";

export function App() {
    
    // creating Signal.State count
    const count = useSignal(0);
    
    // creating Signal.Computed countString
    const countString = useSignal(() => (count.get() * 2).toString());
    
    // creating Signal.Computed style
    const style = useSignal(() => {
        const isEven = count.get() % 2 === 0;
        return {
            background: isEven ? 'white' : 'black',
            color: isEven ? 'black' : 'white'
        }
    })

    // creating Signal.Computed text
    const text = useSignal(() => {
        const isWhite = style.get().background === 'white'
        return <div>{isWhite ? 'Background is White' : 'Background is Black'}</div>
    })

    return <div style={{display: 'flex', flexDirection: 'column', alignItems: 'center'}}>
        {/* When user click button it will update the state */}
        <button onClick={() => count.set(count.get() + 1)}>Click here</button>

        {/* Following line will never get auto update*/}
        <div>{countString.get()}</div>

        {/* Following line will get auto update when user click button*/}
        <notifiable.div>{countString}</notifiable.div>

        {/* Following line will get auto update when user click button*/}
        <notifiable.div style={style}>{text}</notifiable.div>
    </div>
}

STEP 4: Encapsulate any Component with a Notifiable :

  • Use Notifiable component to wrap any React Functional or Class Component
  • A React component encapsulated within the Notifiable component will inherently enable its properties and children to utilize Lambda expressions or Signals seamlessly

Example :

import {Notifiable} from "react-hook-signal";

export function App() {
    const count = useSignal(0);
    
    return <div style={{display:'flex',flexDirection:'column',alignItems:'center'}}>
        {/* When user click button it will update the state */}
        <button onClick={() => count.set(count.get() + 1)}>Click here</button>

        {/* Following line will be updated because of count updated*/}
        <Notifiable component={MyComponent} title={() => {
            return count.get() + ' Times'
        }}></Notifiable>
                    
    </div>
}

function MyComponent(props:{title:string}){
    return <div>Here is the title {props.title}</div>
}

Summary

The integration of Signal into the React application can be done in various ways tailored to the needs and complexity of the app.

About

License:MIT License


Languages

Language:TypeScript 95.6%Language:JavaScript 2.4%Language:HTML 2.0%