Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

use-state-with-deps

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

use-state-with-deps

A React hook to use and reset state with dependencies

  • 1.1.2
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
3.5K
increased by29.69%
Maintainers
1
Weekly downloads
 
Created
Source

use-state-with-deps

npm (scoped) Actions Status codecov Renovate semantic-release

A React hook to use state and reset with dependencies.

Usage

useStateWithDeps hook

useState hook with an additional dependency array that resets the state to the initialState param when the dependencies passed in the deps array change.

Example:

import React from "react";
import { useStateWithDeps } from "use-state-with-deps";

function AnimatedComponent({ animationType }) {
  const [animation, setAnimation] = useStateWithDeps(
    getAnimation(animationType),
    [animationType]
  );

  return <div>Current animation: {animation}</div>;
}
Parameters:
  • initialState: The state that will be set when the component mounts or the dependencies change.

    It can also be a function which resolves to the state. If the state is reset due to a change of dependencies, this function will be called with the previous state (undefined for the first call upon mount).

  • deps: Dependencies for this hook that resets the state to initialState

Motivation

There are some scenarios, where the state of a component is derived from its props and needs to be reset upon an update of the incoming props. For class based components, this could be achieved with the getDerivedStateFromProps lifecycle method. With hooks however, this currently can't be achieved out of the box since the state can't be reset easily without triggering another re-render. Let's look at the example of the documentation of how to migrate getDerivedStateFromProps to hooks:

function ScrollView({ row }) {
  const [isScrollingDown, setIsScrollingDown] = useState(false);
  const [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) {
    // Row changed since last render. Update isScrollingDown.
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }

  return `Scrolling down: ${isScrollingDown}`;
}

Here, isScrollingDown is returned which was based on prevRow, although the correct value would be prevRow !== null && row > prevRow. While react will re-render before continuing, the current render method will continue, because the execution is synchronous. This is especially problematic when using hooks and expecting the result to be consistent with its input.

Let's look at a component where transferring the example from the documentation 1 to 1 would lead to issues:

function getAnimationFromType(type) {
  switch (type) {
    case "Scale":
      return { scale: { x: 0, y: 0 } };
    case "Rotate":
      return { rotate: { deg: 0 } };
    default:
      throw new Error("Invalid Type");
  }
}

function useAnimation(type) {
  const [animation, setAnimation] = useState(getAnimationFromType(type));
  const [prevType, setPrevType] = useState(type);

  if (prevType !== type) {
    setAnimation(getAnimationFromType(type));
    setPrevType(type);
  }

  useEffect(() => {
    // TODO: Animate
  }, [animation]);

  return animation; // Warning! This returns an object with properties that don't match the type!
}

function MyComponent({ type }) {
  const animation = useAnimation(type);

  // Let's assume we want to work with a value that has been returned
  // from the hook in the render function. We might receive an Exception, since
  // the returned value from the useAnimation hook might not be in-sync
  // with our type prop.
  let valueFromAnimationHook;
  switch (type) {
    case "Scale":
      // ERROR: This will throw if the type changed, since animation is still based
      // on "Rotate"
      valueFromAnimationHook = animation.scale.x + animation.scale.y;
      break;
    case "Rotate":
      // ERROR: This will throw if the type changed, since animation is still based
      // on "Scale"
      valueFromAnimationHook = animation.rotate.deg;
      break;
    default:
      break;
  }

  return <OtherComponent animation={animation} />;
}

In this example, an exception is thrown when the type changes, since the returned value by the hook is based on a previous prop. This could be fixed by making the state variable re-assignable:

function useAnimation(type) {
  let [animation, setAnimation] = useState(getAnimationFromType(type));
  const [prevType, setPrevType] = useState(type);

  if (prevType !== type) {
    const newAnimation = getAnimationFromType(type);
    setAnimation(newAnimation);
    animation = newAnimation;

    setPrevType(type);
  }

  useEffect(() => {
    // TODO: Animate
  }, [animation]);

  return animation;
}

This however adds a lot of code and additional complexity. It also triggers another re-render after the state is updated, although it would theoretically not be needed (since the new state is already returned). Therefore this library provides the useStateWithDeps hook to achieve the same outcome with less code and remove the potential of returning a stale value.

With useStateWithDeps, the previous hook can then be rewritten as:

import { useStateWithDeps } from "use-state-with-deps";

function useAnimation(type) {
  const [animation, setAnimation] = useStateWithDeps(
    getAnimationFromType(type),
    [type]
  );

  useEffect(() => {
    // TODO: Animate
  }, [animation]);

  return animation;
}

Requirements

  • React 16.8 or higher
  • A JavaScript environment or polyfill that supports Object.is

FAQs

Package last updated on 23 Jun 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc