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

@utilityjs/use-get-latest

v2.0.0

Published

A React hook that stores & updates `ref.current` with the most recent value.

Readme

UtilityJS | useGetLatest

A React hook that stores & updates ref.current with the most recent value.

Features

  • Stale Closure Prevention: Avoid stale closures in callbacks and async operations
  • Stable References: Create stable callback references without exhaustive dependencies
  • SSR Compatible: Uses isomorphic layout effect for server-side rendering compatibility
  • TypeScript Support: Full type safety with generic support
  • Performance Optimized: Minimal re-renders by avoiding dependency arrays
  • Simple API: Easy to use with any value type

Installation

npm install @utilityjs/use-get-latest

or

pnpm add @utilityjs/use-get-latest

Usage

Basic Example - Avoiding Stale Closures

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useState, useCallback } from "react";

function SearchComponent({ onSearch, query }) {
  const latestOnSearch = useGetLatest(onSearch);
  const latestQuery = useGetLatest(query);

  const handleSearch = useCallback(async () => {
    // These values are always fresh, even if props change
    // after this callback was created
    const results = await searchAPI(latestQuery.current);
    latestOnSearch.current(results);
  }, []); // No dependencies needed!

  return <button onClick={handleSearch}>Search for: {query}</button>;
}

Interval with Dynamic Values

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useState, useEffect } from "react";

function Counter({ step, isRunning }) {
  const [count, setCount] = useState(0);
  const latestStep = useGetLatest(step);
  const latestIsRunning = useGetLatest(isRunning);

  useEffect(() => {
    const interval = setInterval(() => {
      if (latestIsRunning.current) {
        setCount(c => c + latestStep.current);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, []); // No need to restart interval when step or isRunning changes

  return (
    <div>
      <p>Count: {count}</p>
      <p>Step: {step}</p>
      <p>Running: {isRunning ? "Yes" : "No"}</p>
    </div>
  );
}

Event Handlers with Latest State

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useState, useCallback } from "react";

function FormComponent({ onSubmit, validationRules }) {
  const [formData, setFormData] = useState({});
  const latestFormData = useGetLatest(formData);
  const latestValidationRules = useGetLatest(validationRules);
  const latestOnSubmit = useGetLatest(onSubmit);

  const handleSubmit = useCallback(async e => {
    e.preventDefault();

    // Always use the latest values
    const isValid = validateForm(
      latestFormData.current,
      latestValidationRules.current,
    );

    if (isValid) {
      await latestOnSubmit.current(latestFormData.current);
    }
  }, []); // Stable callback reference

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      <button type="submit">Submit</button>
    </form>
  );
}

WebSocket with Latest Handlers

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useEffect } from "react";

function WebSocketComponent({ onMessage, onError, url }) {
  const latestOnMessage = useGetLatest(onMessage);
  const latestOnError = useGetLatest(onError);

  useEffect(() => {
    const ws = new WebSocket(url);

    ws.onmessage = event => {
      // Always calls the latest onMessage handler
      latestOnMessage.current(JSON.parse(event.data));
    };

    ws.onerror = error => {
      // Always calls the latest onError handler
      latestOnError.current(error);
    };

    return () => ws.close();
  }, [url]); // Only reconnect when URL changes

  return <div>WebSocket connection active</div>;
}

Debounced Function with Latest Value

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useState, useCallback, useRef } from "react";

function SearchInput({ onSearch, debounceMs = 300 }) {
  const [query, setQuery] = useState("");
  const latestQuery = useGetLatest(query);
  const latestOnSearch = useGetLatest(onSearch);
  const timeoutRef = useRef<NodeJS.Timeout>();

  const debouncedSearch = useCallback(() => {
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }

    timeoutRef.current = setTimeout(() => {
      // Uses the latest query and onSearch function
      latestOnSearch.current(latestQuery.current);
    }, debounceMs);
  }, [debounceMs]); // Only recreate when debounce time changes

  const handleInputChange = e => {
    setQuery(e.target.value);
    debouncedSearch();
  };

  return (
    <input
      type="text"
      value={query}
      onChange={handleInputChange}
      placeholder="Search..."
    />
  );
}

Animation with Latest Values

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useState, useEffect } from "react";

function AnimatedComponent({ targetValue, duration, onComplete }) {
  const [currentValue, setCurrentValue] = useState(0);
  const latestTargetValue = useGetLatest(targetValue);
  const latestOnComplete = useGetLatest(onComplete);

  useEffect(() => {
    let animationId: number;
    const startTime = Date.now();
    const startValue = currentValue;

    const animate = () => {
      const elapsed = Date.now() - startTime;
      const progress = Math.min(elapsed / duration, 1);

      // Always animate towards the latest target value
      const target = latestTargetValue.current;
      const newValue = startValue + (target - startValue) * progress;

      setCurrentValue(newValue);

      if (progress < 1) {
        animationId = requestAnimationFrame(animate);
      } else {
        // Call the latest completion handler
        latestOnComplete.current?.(target);
      }
    };

    animationId = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(animationId);
  }, [duration]); // Only restart animation when duration changes

  return (
    <div>
      Current: {currentValue.toFixed(2)} / Target: {targetValue}
    </div>
  );
}

Custom Hook with Latest Values

import { useGetLatest } from "@utilityjs/use-get-latest";
import { useEffect, useState } from "react";

function useAsyncData<T>(
  fetchFn: () => Promise<T>,
  dependencies: any[],
  onSuccess?: (data: T) => void,
  onError?: (error: Error) => void,
) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const latestFetchFn = useGetLatest(fetchFn);
  const latestOnSuccess = useGetLatest(onSuccess);
  const latestOnError = useGetLatest(onError);

  useEffect(() => {
    let cancelled = false;

    const fetchData = async () => {
      setLoading(true);
      setError(null);

      try {
        // Always use the latest fetch function
        const result = await latestFetchFn.current();

        if (!cancelled) {
          setData(result);
          latestOnSuccess.current?.(result);
        }
      } catch (err) {
        if (!cancelled) {
          const error = err as Error;
          setError(error);
          latestOnError.current?.(error);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    };

    fetchData();

    return () => {
      cancelled = true;
    };
  }, dependencies);

  return { data, loading, error };
}

API

useGetLatest<T>(value)

Parameters

  • value: T - The value to store and keep updated in the ref

Returns

  • RefObject<T> - A ref object with a .current property containing the latest value

Type Parameter

  • T - The type of the value being stored

Behavior

  1. Initial Value: The ref is initialized with the provided value
  2. Updates: The ref is updated with the latest value on every render using useLayoutEffect
  3. SSR Safe: Uses useEffect on the server and useLayoutEffect on the client
  4. Synchronous: Updates happen synchronously during the render phase (client-side)

Use Cases

  • Stale Closures: Prevent stale closures in event handlers, intervals, and async operations
  • Stable Callbacks: Create stable callback references without exhaustive dependency arrays
  • Latest Values: Access the most recent value in long-running operations
  • Performance: Avoid unnecessary re-renders by reducing dependency arrays

Best Practices

  1. Use for Callbacks: Ideal for event handlers and callback functions that need latest values
  2. Async Operations: Perfect for accessing latest state in promises, intervals, and timeouts
  3. Stable References: Use to create stable function references without dependencies
  4. Don't Overuse: Only use when you specifically need to avoid stale closures
  5. Combine with useCallback: Often used together with useCallback for stable, latest-value callbacks

Common Patterns

// Pattern 1: Stable callback with latest values
const latestValue = useGetLatest(value);
const stableCallback = useCallback(() => {
  doSomething(latestValue.current);
}, []); // No dependencies needed

// Pattern 2: Async operation with latest state
const latestState = useGetLatest(state);
useEffect(() => {
  const asyncOperation = async () => {
    const result = await api.call();
    // latestState.current always has the latest value
    updateUI(latestState.current, result);
  };
  asyncOperation();
}, [trigger]); // Only depend on trigger, not state

// Pattern 3: Event handler with latest props
const latestProps = useGetLatest(props);
const handleEvent = useCallback(event => {
  processEvent(event, latestProps.current);
}, []); // Stable reference

Contributing

Read the contributing guide to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes.

License

This project is licensed under the terms of the MIT license.