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 🙏

© 2026 – Pkg Stats / Ryan Hefner

hookstand

v0.0.5

Published

Robust & opinionated React state management with hooks

Downloads

18

Readme

Hookstand

Opinionated React state management with hooks 🪝

⚡ Powered by Zustand & Immer

NOTE: This project is very 🚧 WIP 🚧, and was created out of passion to simplify my own development experience on React state management. Any feedback/help/advice is greatly appreciated!

example of usage

Installation

npm i hookstand

tsconfig.json

{
  "compilerOptions": {
    "strict": true, // Required
    "strictNullChecks": true // Recommended
  }
}

Usage

In its most basic form, Hookstand can be used to replace all useState & useCallback hooks.

import {
  useAction,
  useInput,
  useStore,
  useSubstate,
  useWatch,
} from 'hookstand';

const Login = () => {
  // 👉 Create a store locally within a component
  const { store } = useStore({
    email: '',
    password: '',
    isLoggedIn: false,
  });

  // 👉 Listen to changes in certain properties & only update when necessary
  const { isLoggedIn, isLoading, error } = useSubstate(store, [
    'isLoggedIn',
    // ℹ️ Every Hookstand store comes with isLoading & error properties included
    'isLoading',
    'error',
  ]);

  // 👉 Create plug-and-play prop collections to make handling inputs easy
  const emailProps = useInput(store, 'email');
  const passwordProps = useInput(store, 'password');

  // 👉 Listen for changes in the built-in "error" value & alert the user if it changes
  useWatch(store, 'error', (error) => {
    if (error === null) return;
    alert(`An error occurred: ${error}`);
  });

  // 👉 Create an action that can effect state
  // ℹ️ If the action is asynchronous, isLoading will be true while the Promise is pending
  //    If the promise rejects, error will be set to "unexpected error"
  const handleLogin = useAction(store, (state) => async () => {
    const { email, password } = state;

    const response = await fetch('https://example.com/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    }).then((response) => response.json());

    if (response.success) {
      // ℹ️ Every action is provided with an Immer draft of the current state,
      //    so its properties can be modified directly
      state.isLoggedIn = true;
    } else {
      state.error = 'invalid credentials';
    }
  });

  return (
    <article>
      <h1>Sign In</h1>

      {/* 👉 Switch content based on listened state properties */}
      {isLoggedIn && <p>You ARE currently logged in 🔓</p>}
      {!isLoggedIn && <p>You ARE NOT currently logged in 🔒</p>}

      <div>
        {/* 👉 Spread props to inputs */}
        <input type="email" label="Email" {...emailProps} />
        <input type="password" label="Password" {...passwordProps} />

        {/* 👉 Use the built-in error state to easily show error text when necessary */}
        {!!error && <b>Something went wrong, please try again…</b>}

        {/* 👉 Use the handleLogin action as an onClick listener */}
        {/* ℹ️ isLoading can be used to disable the button while the action is executing */}
        <button disabled={isLoading} onClick={handleLogin}>
          Sign In
        </button>
      </div>
    </article>
  );
};

Hooks

useStore

🚧 TODO

useSubstate

Allows picking & returning values from the store object. Only triggers an update when one of the picked values changes. The returned value/object will have the correct TypeScript type.

Automatically displays all possbile paths to pick from in any IDE that supports TypeScript code completion.

NOTE: Nested path discovery works for most value types, but some are disabled due to performance reasons.
Current disabled types:

  • Class
  • Date
  • HTMLElement

Examples

// Pick a single value
const foo = useSubstate(store, 'foo');

// Pick multiple values
const foo = useSubstate(store, ['foo', 'bar']);

// Pick a nested value
const bar = useSubstate(store, 'foo.bar');

// Pick a custom value using a selector function
const [foo, bar] = useSubstate(store, (state) => [state.foo, state.bar]);

useInput

🚧 TODO

useAction

🚧 WIP

Actions are functions that can directly update state. They can be used the same way a React useCallback hook is used, but often do not require any dependencies to be specified.

An action receives an immer draft as the argument for the outer function. This state can be modified directly without changing the immutable nature of the state elsewhere.

NOTE: The state draft is essentially a snapshot of the state from when the async function was excecuted at. You can use the store.getState() function to get the current state inside an async action in case it was updated while the action was running, although this should be very rarely required.

NOTE: An async action function will have the draft state applied to the actual state once it has finished executing. If you need to update the state "live" while the function is running, the outer function also gets a "set" function along the state draft that can be used to update the state immediately. An example of this is provided below.

NOTE: If the action function is asynchronous, it will not have the "isLoading" property set to true in its state draft.

// ℹ️ If the action is asynchronous, isLoading will be true while the Promise is pending
//    If the promise rejects, error will be set to "unexpected error"
const handleLogin = useAction(
  store,
  (state) => async () => {
    const { email, password } = state;

    const response = await fetch('https://example.com/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    }).then((response) => response.json());

    if (response.success) {
      // ℹ️ Every action is provided with an Immer draft of the current state,
      //    so its properties can be modified directly
      state.isLoggedIn = true;
    } else {
      state.error = 'invalid credentials';
    }
  },
  // ℹ️ An action can have "outside" dependencies like the useCallback hook
  //    They can be specified as an array, just like for useCallback
  //    This array can be omitted if no dependencies are required
  //    You do not need to specify the store (or any parts of its state) as a dependency
  []
);

// ℹ️ An example of a asynchronous action that has to update state immediately to provide upload progress
const handleUpload = useAction(
  store,
  // ℹ️ An optional "set" function is also provided here that can be used to update state immediately
  (state, set) => async () => {
    let complete = false;
    do {
      const { progress, complete: isComplete } = await fetch('...', { ... });
      complete = isComplete;

      // ℹ️ Update the "progress" state value immediately
      set({ progress });
    } while (!complete)
  },
  []
);

useWatch

🚧 TODO

React Native

React Native is fully supported, and does not require any extra installation steps.

A special useNativeInput is also available to make binding React Native inputs to state easier.

useNativeInput

Identical to useInput, but uses the onChangeText prop to listen to changes.

Examples

import { useNativeInput, useStore } from 'hookstand';

// ...

const { store } = useStore({
  email: '',
});

// Swap the "useInput" hook for the "useNativeInput" hook
const emailProps = useNativeInput(store, 'email');

// Apply props to the input
return <TextInput {...emailProps} />;