Boris Traljić
Boris Traljić

Reputation: 956

Unexpected(?) behavior of reusable React hook functions or what am I doing wrong?

I am trying to write my own reusable useEffect hook function useEffectOnce() to run only once (on mount and unmount), but it seems impossible. My function is called after every re-render: https://codesandbox.io/s/mjx452lvox

function useEffectOnce() {
  console.log("I am in useEffectOnce");

  useEffect(() => {
    console.log("I am in useEffectOnce's useEffect");
    return () => {
      console.log("i am leaving useEffectOnce's useEffect");
    };
  }, []);
}

function App() {
  const [count, setCount] = useState(0);

  useEffectOnce(); // once!?

  return (
    <div className="app">
      <h3>useEffectOnce (my reusable function)... Wait, once!?</h3>
      <button onClick={() => setCount(count + 1)}>
        You clicked me {count} times
      </button>
    </div>
  );
}

Notes:

... and then big surprise, same behavior with reusable useState hook function(!?): https://codesandbox.io/s/2145m37xnr

My function useButton is called after every re-render, and when is first, independent, button clicked.

function useButton(initialCounterValue) {
  const [usebuttoncount, setUseButtonCount] = useState(initialCounterValue);

  console.log("I am in useButton");

  const handleuseButtonClick = () => {
    setUseButtonCount(usebuttoncount + 1);
  };

  return {
    usebuttoncount,
    onClick: handleuseButtonClick
  };
}

function App() {
  const [count, setCount] = useState(0);
  const useButtonCounter = useButton(0);

  return (
    <div className="app">
      <h3>
        useButton (my reusable useState function) is called only when... OMG!?
      </h3>
      <button onClick={() => setCount(count + 1)}>
        You clicked me {count} times
      </button>
      <br />
      <button {...useButtonCounter}>
        (useButton hook) You clicked me {useButtonCounter.usebuttoncount} times
      </button>
    </div>
  );
}

Upvotes: 1

Views: 493

Answers (1)

Estus Flask
Estus Flask

Reputation: 222464

useEffectOnce and useButton are regular functions. They are expected to run on every component render. When component function is called, there's no way to prevent them from being called by conventional JavaScript means (i.e. with no eval).

It's useEffect callback function (I am in useEffectOnce's useEffect log entry) that is expected to be called once on component mount. All code that needs to be evaluated on component mount should reside there.

Upvotes: 1

Related Questions