Home > Blockchain >  Run helper function in a hook only when some hook state changes
Run helper function in a hook only when some hook state changes

Time:12-24

How can I adjust the code below so that the log message console.log("This should log only on resetting the number") is only logged when "Reset number" button is clicked, not when "Rerender" button is clicked?

import * as React from "react";
import { render } from "react-dom";

const getNumber = () => Math.round(Math.random() * 100);

const useNumber = () => {
  const [value, setValue] = React.useState<number>(getNumber());

  const isLargerThan = React.useMemo(() => {
    const func = (i: number) => {
      console.log("This should log only on resetting the number");
      return value > i;
    };
    return func;
  }, [value]);

  const reset = () => setValue(getNumber());

  return { value, isLargerThan, reset };
};

const App = () => {
  const number = useNumber();
  const [trigger, setTrigger] = React.useState(false);

  console.log("rerendering");

  return (
    <div>
      <div>
        Is {number.value} larger than 50?: {String(number.isLargerThan(50))}
      </div>
      <button onClick={number.reset}>Reset number</button>
      <button onClick={() => setTrigger(!trigger)}>Rerender</button>
    </div>
  );
};
const rootElement = document.getElementById("root");
render(<App />, rootElement);

LIVE EXAMPLE: https://codesandbox.io/s/react-ts-playground-forked-2urxn2?file=/src/index.tsx:305-390

CodePudding user response:

From what I understand, you are calling your useCallback function directly in your render method, so everytime you run a re-render, the function gets called once again. One way to avoid this would be to not have your useMemo return a function but rather a simple comparison. You could also initialize your hook with a value to store the number you are comparing to and use it at a useMemo dependency. I made something looking like this, which seems to be working as you intend.

const getNumber = () => Math.round(Math.random() * 100);

const useNumber = (val) => {
  const [value, setValue] = React.useState<number>(getNumber());

  const isLargerThan = React.useMemo(() => {
    console.log("This should log only on resetting the number");
    return value > val;
  }, [value, val]);

  const reset = () => setValue(getNumber());

  return { value, isLargerThan, reset };
};

const App = () => {
  const [val, setVal] = React.useState(50)
  const number = useNumber(val);
  const [trigger, setTrigger] = React.useState(false);

  console.log(number.isLargerThan)
  return (
    <div>
      <div>
  Is {number.value} larger than {val}?: {String(number.isLargerThan)}
      </div>
      <button onClick={number.reset}>Reset number</button>
      <button onClick={() => {setTrigger(!trigger)}}>Rerender</button>
    </div>
  );
};
const rootElement = document.getElementById("root");
render(<App />, rootElement);

CodePudding user response:

There is a really easy solution to achieve the desired result:

  const isLargerThan = React.useCallback((i: number) => {
      console.log("updating the stored function");
      return value > i;
    }, [value]);

You should try useCallback because it will update the function you pass it only when value updates. Doing this you can call the function to display the right markup:

<div>
  Is {number.value} larger than 50?: {String(number.isLargerThan(50))}
</div>
  • Related