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-usesavedcallback

v1.0.0

Published

useRef and useCallback to work with external JavaScript

Downloads

167

Readme

react-useSavedCallback

Invoke a callback that is saved from the latest render

Usage

Install with npm or yarn

npm i react-useSavedCallback
yarn add react-useSavedCallback

Use this function instead of useCallback when you are interfacing with external JavaScript (trivial example is setInterval).

const [a, setA] = useState(1);

const func = useSavedCallback(() => {
    setA(a + 1);
    console.log(a);
}, [a]);

useEffect(() => {
    const interval = setInterval(func, 1000);

    return () => clearInterval(interval);
}, []);

Background

Working with callbacks from 3rd party dependencies and React Hooks can be a little bit confusing.

The classic example is setInterval built in.

Example:

const [a, setA] = useState(1);

useEffect(() => {
    const interval = setInterval(() => {
        setA(a + 1);
        console.log(a);
    }, 1000);

    return () => clearInterval(interval);
}, []);

The callback passed into setInterval will be the rendered function at that point in time. This means a will not change as it was collected at the point of render (when useEffect fired);

It would be nice if you could use the useCallback function to update:

const [a, setA] = useState(1);

const func = useCallback(() => {
    setA(a + 1);
    console.log(a);
}, [a]);

useEffect(() => {
    const interval = setInterval(func, 1000);

    return () => clearInterval(interval);
}, []);

The problem however is still the same. func is pointing towards the function returned by useCallback during that initial render. Of course, useCallback is great for only re-creating complex functions if a dependency changes, but it will return the function created at that point in time. It doesn't provide us a method to dynamically update the callback.

There are a few solutions to this problem, including this cool library: https://github.com/Aminadav/react-useStateRef

However, even though you can use this to access the state via the ref to get the current value, it doesn't help you when you want to propagate up the chain.

Consider you have a prop passed in, which is a callback function from this child component:

const { parentFunc } = props;

const [a, setA] = useState(1);

const func = useCallback(() => {
    setA(a + 1);
    console.log(a);

    parentFunc(a);
}, [a]);

useEffect(() => {
    const interval = setInterval(func, 1000);

    return () => clearInterval(interval);
}, []);

This callback will point to parentFunc at the point in time of this render, and therefore you will also "see" the state of the parent component at the point in time that the render was done. This means you would have to access all your state via state refs all the way up the chain and life gets messy.

Anyway, wouldn't it be great to just move the ref of the callback? Well, we can! Mix the magic of useStateRef and this blog: https://overreacted.io/making-setinterval-declarative-with-react-hooks/ and we end up with a super simple generic hook to move the reference to the callback, instead of the reference to the state. Also, a touch of syntatic suger means we don't have to worry about .current.

Enter this hook - this is the entirety of it:

import { useCallback, useEffect, useRef } from 'react';

const useSavedCallback = (callbackFunc, deps) => {
    const savedCallback = useRef();

    const callback = useCallback(callbackFunc, deps);

    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);

    return (...params) => savedCallback.current(...params);
};

export default useSavedCallback;

All we are doing is updating the ref to the callback whenever it changes.

Just use useSavedCallback instead of useCallback

const [a, setA] = useState(1);

const func = useSavedCallback(() => {
    setA(a + 1);
    console.log(a);
}, [a]);

useEffect(() => {
    const interval = setInterval(func, 1000);

    return () => clearInterval(interval);
}, []);

This code will now behave as expected.

Thanks to this awesome blog for making this clear to me - and it has helped loads with interfacing with external callback style JavaScript from within React: https://overreacted.io/making-setinterval-declarative-with-react-hooks/