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 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-perf-state

v0.0.3

Published

Performance states with queues for React

Readme

react-perf-state

Tiny performance-oriented React hook for coalescing multiple setState calls into a single update per animation frame.

When a component calls setState many times in quick succession (e.g., during scroll/gesture/resize or bursty events), React may still perform several renders.
useQueueState batches those updates per component and applies exactly once per requestAnimationFrame, cutting down renders and layout thrash.


✨ Features

  • rAF-batched: merges all updates issued in the same frame.
  • Per-component queues: each component gets its own queue; parallel components won’t step on each other.
  • Functional updates compose: multiple (prev) => next calls are applied in order, like a reducer.
  • TypeScript-first: full generics and strong typing.
  • Drop-in: same API shape as useState (tuple [state, setState]).

Installation

npm i react-perf-state
# or
yarn add react-perf-state
# or
pnpm add react-perf-state

No peer deps beyond React.


Quick start

import { useQueueState } from 'react-perf-state';

function Counter() {
  const [count, setCount] = useQueueState(0);

  const burst = () => {
    // These three updates will be merged into a single render this frame
    setCount(c => c + 1);
    setCount(c => c + 1);
    setCount(c => c + 1);
  };

  return (
    <button onClick={burst}>
      Count: {count}
    </button>
  );
}

API

useQueueState<T>(initial: T | (() => T))

Returns [state, queuedSetState] just like useState.

Behavior details

  • rAF scheduling: the first call to queuedSetState in a frame schedules a requestAnimationFrame that will flush the queue.
  • Composition: if you call queuedSetState multiple times before the rAF flush, they’re reduced in order.
  • Per-instance queue: queue is keyed by the component’s setState reference.
  • State ref safety: ensures correct base state even if queue starts with undefined.

Why not just rely on React 18 batching?

React 18 batches state updates within the same event. Many high-frequency scenarios (pointermove, scroll, timers, observers) can still trigger multiple updates/renders across frames.
useQueueState guarantees at most one render per frame per component regardless of how many setStates you fire.


Examples

1) Smooth pointer tracking

function PointerTracker() {
  const [pt, setPt] = useQueueState({ x: 0, y: 0 });

  useEffect(() => {
    const onMove = (e: PointerEvent) => {
      setPt(curr => ({ ...curr, x: e.clientX, y: e.clientY }));
    };
    window.addEventListener('pointermove', onMove, { passive: true });
    return () => window.removeEventListener('pointermove', onMove);
  }, [setPt]);

  return <div>({pt.x}, {pt.y})</div>;
}

2) Coalescing expensive derived state

setFilters(f => ({ ...f, price: 'low' }));
setFilters(f => ({ ...f, rating: 4 }));
setFilters(f => ({ ...f, inStock: true }));
// -> One render with fully combined filters

3) React Native scrolling

<FlatList
  onScroll={e => {
    const y = e.nativeEvent.contentOffset.y;
    setScrollState(s => ({ ...s, y }));
  }}
  scrollEventThrottle={16}
/>

Caveats & best practices

  • Not for controlled text inputs: use plain useState for keystroke-bound values.
  • rAF availability (SSR / background tabs): provide polyfills if needed.
  • No synchronous reads: updates apply later, not instantly.
  • Same rules as React state: don’t throw from update functions.

License

MIT © contributors