React Hooks setState in useEffect once on ref mount

  const [x, setX] = useState();
  const [y, setY] = useState();

  const elementRef = useRef(null);

  // Calculated here based on elementRef (null on initial render, non-null after mount)
  let boundTop;
  let boundRight;
  let boundBottom;
  let boundLeft;

  // Center the stage on mount and ref mount
  useEffect(() => {
    const x = (window.innerWidth / 2) - ((boundRight - boundLeft) / 2);
    const y = (window.innerHeight / 2) - ((boundBottom - boundTop) / 2);
    setX(x);
    setY(y);
  }, [elementRef.current]);

This code works fine and as intended - on initial render and ref mount ([elementRef.current] dependency array), it will set the state of X and Y to some initial values and then never execute again.

But! The default Hooks ESLint rules nudge me to add the bound state variables as dependencies to the useEffect dependency array. I don't wanna do that! I don't want those values to change again through this hook after the initial mount.

If I did add the bound values as dependencies to the effect, my canvas could never move, because each move to it (by changing X and Y in response to mouse/touch) would cause a render, thus bounds would be recalculated, the hook dependencies changed, X and Y centered and the original movement of X and Y which caused the canvas to move would become undone.

Is there a pattern to use to code around this ESLint rule?

Upvotes: 1

Views: 873

Answers (1)

Bennett Dams
Bennett Dams

Reputation: 7033

The useState allows you to set its initial value via a callback, so you don't need your useEffect at all.

Change your useState like so and remove your whole useEffect:

const [x, setX] = useState(() => (window.innerWidth / 2) - ((boundRight - boundLeft) / 2));
const [y, setY] = useState(() => (window.innerHeight / 2) - ((boundBottom - boundTop) / 2));

Upvotes: 2

Related Questions