Unexpected behaviour of useState hook

Viewed 150

I was trying out an image mapper library and found an unexpected behaviour. The handler function handleInc behaves differently depending on whether its triggered by the + button or via clicking on the highlighted area in the image (need to move mouse around in order for highlighted area to appear).

When using + button the behaviour is as expected but when clicking highlighted area in the image, the state variable count doesn't seems to go beyound 1.

What's the reason for behind the same function handleInc behaving differently.

Here is the code (sandbox for the code below)

import { useState } from "react";
import "./styles.css";
// https://github.com/img-mapper/react-img-mapper
import ImageMapper from "react-img-mapper";

const URL =
  "https://helpx.adobe.com/content/dam/help/en/stock/how-to/visual-reverse-image-search/jcr_content/main-pars/image/visual-reverse-image-search-v2_intro.jpg";

export default function App() {
  const [count, setCount] = useState(0);
  const handleInc = () => {
    // this print expected value when + button is clicked
    // but print 0 if highlighted area is clicked
    console.log(count);
    setCount(count + 1);
  };
  const handleDec = () => {
    console.log(count);
    setCount(count - 1);
  };
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick={handleInc}>+</button>
      {count}
      <button onClick={handleDec}>-</button>

      <ImageMapper
        src={URL}
        map={{
          name: "asdf",
          areas: [
            {
              id: 0,
              active: true,
              title: "BB(1-4)",
              shape: "poly",
              name: "BB(1-4)",
              fillColor: "#eab54d4d",
              strokeColor: "black",
              coords: [
                260,
                142,
                77,
                196,
                184,
                530,
                840,
                529,
                928,
                283,
                894,
                26,
                389,
                53,
                343,
                31,
                321,
                90
              ]
            }
          ]
        }}
        stayMultiHighlighted
        onClick={handleInc}
      />
    </div>
  );
}
2 Answers

Seems that the ImageMapper component is memoizing the callbacks, in other words, it has closed over the initial count state value and never updates from that point.

If you use a functional state update then clicking the highlighted area seems to work as I think you expect it to. Functional state updates work by updating from the previous state versus the state from render cycle the callback was enqueued in.

const handleInc = () => {
  setCount(count => count + 1);
};

const handleDec = () => {
  setCount(count => count - 1);
};

Demo

Edit unexpected-behaviour-of-usestate-hook

Related