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

use-method

v1.0.0

Published

`yarn add use-method`

Downloads

3

Readme

Useage

yarn add use-method

import { useMethod } from 'use-method';

useMethod(callbackFunction); // returns function with the same signature as callbackFunction

Introduction & Rationale

When hooks got introduced - it's became problematic to avoid re-creating functions references on each render.

With classes it's not a problem. You just call this.handleClick which has the same reference on every render.

With functional components, you define such functions during rendering like

function SomeComponent() {
  function handleClick() {
    // handling click
  }

  // rendering
}

Without any optimization - handleClick would be brand new function on every render, which can lead to re-rendering child components.

React useCallback hook is created to help solving this problem.

However, when using it, you might face some problems:

Polluted code which doesn't contribute to business logic:

Quite often, when creating more complicated components, you'll end up having quite long list of callback dependencies:

const onDragMove = useCallback(() => {
  // implementation
}, [
  onDragStart,
  onDragEnd,
  onDragSort,
  currentDraggableItems,
  currentDraggedItem,
]);

It 'pollutes' your code without contributing to actual business logic.

It becomes even worse if your list of dependencies becomes so long that formatter like prettier breaks the list into multiple lines.

Code lines related to those dependencies can actually be quite big part of total lines of code of your file.

Also - without using proper linter - it's very easy to forget about adding some dependency which might lead to nasty bugs. You can also provide too many dependencies at some point (eg. when you'll refactor your code) which can loosen your optimizations.

useCallback result reference still has to update when dependencies change

Even when properly using useCallback - your function reference still has to update when dependencies change. eg:

function PeopleToggler({ people, onToggle }) {
  const togglePerson = useCallback(
    (person) => {
      const peopleAfterToggle = oggleInArray(people, person);
      onToggle(peopleAfterToggle);
    },
    [people, onToggle],
  );

  return <ToggleButton onToggle={togglePerson} />;
}

In this case, our togglePerson will actually get new reference every time props {people, onToggle} will change. It seems unavoidable with useCallback without creating ref value for every value used inside the callback.

It means our ToggleButton will also re-render every time main props will change.

What can we do about it?

First, let's consider this:

There are actually 2 main ways in which functions passed as props are used in React components.

One way is to call a function from props DURING the render:

function UserFollowersCount({ followersFilter, followersList }) {
  const actualFollowers = followersList.filter((follower) => {
    // !! Our function from props is called DURING the render
    return followersFilter(follower);
  });

  // render the list
}

In this case - function provided in prop (followersFilter) can impact render result so component needs to be aware when it has changed in order to re-render.

In this case it's good thing that followersFilter will get new reference as we indeed want it to re-render.

We also have functions from props called only AFTER the render (usually inside events)

Quite often we pass a lot of functions (usually related to events) that got called at some point in time AFTER the render (they can also never be actually called)

function Input({placeholder, onChange}) {
  return <input placeholder={placeholder} onChange={event => {
    // onChange function from props get called on some event after the render
    // it might also never be actually called
    onChange(event.target.value)
  }}>
}

In this case, provided function in props doesn't impact render result as it's only used AFTER the render when some action (like onClick/onChange) occurs.

What it means in terms of optimizations is we want to keep this prop function reference the same as long as possible while allowing it to have access to fresh values used inside it.

And this is exactly what useMethod is doing.

How does it work?

useMethod will return the same callback function reference during entire lifecycle of the component, but under the hood it'll always use last function version, when called.

Let's consider such example:

function PeopleToggler({ people, onToggle }) {
  const togglePerson = useMethod((person) => {
    // we want to always use `fresh` people and onToggle props here
    const peopleAfterToggle = oggleInArray(people, person);
    onToggle(peopleAfterToggle);
  });

  return <ToggleButton onToggle={togglePerson} />;
}

When togglePerson is called - we need access to fresh people and onToggle values in order for callback to work properly.

At the same time, we want togglePerson reference to remain constant to avoid re-rendering ToggleButton.

Both of those things will happen with useMethod.

The same component using useCallback could look like:

function PeopleToggler({ people, onToggle }) {
  const togglePerson = useCallback(
    (person) => {
      // we need fresh `people` and `onToggle` here
      const peopleAfterToggle = oggleInArray(people, person);
      onToggle(peopleAfterToggle);
    },
    [people, onToggle],
  );

  return <ToggleButton onToggle={togglePerson} />;
}

In this case, our togglePerson will actually get new reference every time props people or onToggle props will change.

It means our ToggleButton will also re-render every time those props change.

When to use it and when not to use it?

If some function is called DURING the render block eg.

function PersonCard({ person, avatarRenderer }) {
  return (
    <div>
      <strong>{person.name}</strong>
      <div className="avarar-holder">
        {/** avararRenderer is called DURING the render **/}
        {avatarRenderer(person)}
      </div>
    </div>
  );
}

Dont use this hook. Function returned from it will always have the same reference so child component will not know if it has to re-render

If some function is called AFTER the render block eg.

function Clicker({ label, onClick }) {
  return (
    <div
      onClick={() => {
        // onClick is called AFTER the render (it might actually never get called)
        onClick();
      }}
    >
      {label}
    </div>
  );
}

Use this hook as it's not used called during the render.

Licence

MIT