npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-use-signals

v1.7.7

Published

A generic signals based state management package for React JS

Downloads

13

Readme

React-Use-Signals

This package is aimed to provide a comfortable and easy to use state management solution for React.

It is inspired by Vue's Signals API and is based on their Reactivity in Depth Article.

Installation

For NPM users:

npm install react-use-signals

For Yarn users:

yarn add react-use-signals

Creating a Signal

// counterSignal.js
import { createSignal } from 'react-use-signals';

export const counterSignal = createSignal(0);

export const handleIncrement = () => {
  counterSignal.value += 1;
};

Using a Signal

import { counterSignal, handleIncrement } from './counterSignal';

const Counter = () => {
  const count = counterSignal.useStateAdapter();

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
};

Reactivity

As you can see in the example above, the Counter component is subscribed to the counterSignal and will be re-rendered whenever the signal's value changes.

The example uses the useStateAdapter hook to create a local state for your component that is automatically updated whenever the signal's value changes.

Utility functions like handleIncrement in the example above can be used to update the signal's value and called from anywhere in your application, even outside of React components.

This eliminates the need for prop drilling and setters and makes it easy to share state between components.

Complex Data Structures

Signals can hold any type of data, including complex data structures like objects and arrays.

// userSignal.js

import { createSignal } from 'react-use-signals';

export const userSignal = createSignal({
  name: 'John Doe',
  age: 42,
  address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    zip: '10001',
  },
});

export const handleUpdateName = name => {
  userSignal.value.name = name;
};

export const handleUpdateStreet = street => {
  userSignal.value.address.street = street;
};
import { userSignal, handleUpdateName, handleUpdateStreet } from './userSignal';

const User = () => {
  const user = userSignal.useStateAdapter();

  return (
    <div>
      <p>Name: {user.value.name}</p>
      <p>Street: {user.value.address.street}</p>

      <input
        type="text"
        value={user.value.name}
        onChange={e => handleUpdateName(e.target.value)}
      />
      <input
        type="text"
        value={user.value.address.street}
        onChange={e => handleUpdateStreet(e.target.value)}
      />
    </div>
  );
};

Effects

There are two ways to create effects with Signals.

1. Using the useEffect hook

Signals can be used with the useEffect hook just like any other React state.

import { counterSignal } from './counterSignal';

const Counter = () => {
  const count = counterSignal.useStateAdapter();

  useEffect(() => {
    console.log(`Count: ${count.value}`);
  }, [count.value]);

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
};

2. Using the signalEffect function

The signalEffect function allows you to create a function that will be called whenever the signal's value changes. It is similar to the useEffect hook, but is designed to be used outside of React components.

Furthermore, it automatically knows which signals are used inside the function and doens't require a dependency array.

import { signalEffect } from 'react-use-signals';
import { counterSignal } from './counterSignal';

signalEffect(() => {
  console.log(`Count: ${counterSignal.value}`);
});

Using Signals as a Store

Signals can be used as a store by exporting the signal's value and utility functions from a separate file.

In order to optimize performance, you can use the useSelector hook to subscribe to only the values you need.

This will prevent your component from re-rendering when other properties of the signal's value change.

Please note that the useSelector should only be used if your signals is an object or array. If your signal is a primitive value, you should use the useStateAdapter hook instead.

// user.store.js

import { createSignal } from 'react-use-signals';

export const userSignal = createSignal({
  name: 'John Doe',
  age: 42,
  address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    zip: '10001',
  },
});

export const handleUpdateName = name => {
  userSignal.value.name = name;
};

export const handleUpdateStreet = street => {
  userSignal.value.address.street = street;
};
// UserDetails.jsx

import { userSignal, handleUpdateName, handleUpdateStreet } from './user.store';

const User = () => {
  const username = userSignal.useSelector(value => value.name);

  return (
    <div>
      <p>Name: {username}</p>
      // Updating the name will cause this component to re-render since with selected
      the name property from the signal's value.
      <input
        type="text"
        value={username}
        onChange={e => handleUpdateName(e.target.value)}
      />
      // Updating the street will not cause this component to re-render since we
      did not select the street property from the signal's value.
      <p>Street: {userSignal.value.address.street}</p>
      <input
        type="text"
        value={userSignal.value.address.street}
        onChange={e => handleUpdateStreet(e.target.value)}
      />
    </div>
  );
};

Reseting a signal

Signals have a built-in method called reset that sets the value of the signal to the value it was initialied with. This can come in handy when submitting a form and you want to reset all of the fields

Typescript

Signals can be used with Typescript by providing a type argument to the createSignal function.

import { createSignal } from 'react-use-signals';

type User = {
  name: string;
  age: number;
};

export const userSignal = createSignal<User>({
  name: 'John Doe',
  age: 42,
});

If no type argument is provided, the signal will infer the type from the initial value. This can become problematic when the initial value is null or undefined, or an empty array or object.

The signal itself is a generic type that takes the type of the initial value as an argument.

import { createSignal, Signal } from 'react-use-signals';

type User = {
  name: string;
  age: number;
};

export const userSignal: Signal<User> = createSignal({
  name: 'John Doe',
  age: 42,
});

Deleting Object Properties

As of version 1.7.2, deleting object properties using the delete keyword is supported.

import { createSignal } from 'react-use-signals';

type IMember = {
  name: string;
  age: number;
};

type State = Record<string, IMember>;

export const memberSignal = createSignal<State>({
  '1': {
    name: 'John Doe',
    age: 42,
  },
  '2': {
    name: 'Jane Doe',
    age: 36,
  },
});

const handleDelete = (id: string) => {
  if (memberSignal.value[id])
    delete memberSignal.value[id];
  }
};

Debugging

If you try to console.log a signal's value, you will notice that it is wrapped in a Proxy object. This is because the signal's value is reactive and the Proxy is used to track changes to the value.

If you want to log the actual value, you can use the peek function.

import { counterSignal } from './counterSignal';

console.log(counterSignal.peek());

Behind the Scenes

If you are interested in how this package works, you can check out this article on Medium The Quest for Signals in React. Where I explain how I came up with this solution and how it works under the hood.

Stay in touch

If you have any questions or suggestions, feel free to open an issue on Github.