1

I am new to React and it seems to me that if you use a function inside of useEffect, that entire stack has to be wrapped in useCallback in order to comply with the linter.

For example:

const Foo = ({} => {
  const someRef = useRef(0);

  useEffect(() => {
    startProcessWithRef();
  }, [startProcessWithRef]);

  const handleProcessWithRef = useCallback((event) => {
    someRef.current = event.clientY;
  }, []);

  const startProcessWithRef = useCallback(() => {
    window.addEventListener('mousemove', handleProcessWithRef);
  }, [handleProcessWithRef]);

  ...
});

I'm wondering if there is a different pattern where I don't have to make the entire chain starting in useEffect calling startProcessWithRef be wrapped in useCallback with dependencies. I am not saying it is good or bad, I'm just seeing if there is a preferred alternative because I am new and don't know of one.

tau
  • 6,499
  • 10
  • 37
  • 60
  • Refs are used for storing a html element in a variable, but you seem to use it for a number. Look at [useState](https://reactjs.org/docs/hooks-reference.html#usestate) instead. – Casper Kuethe Jan 26 '22 at 18:40
  • @CasperKuethe this is a slightly contrived example; i just wanted to show that i was using a ref and the actual type of the ref doesnt matter in this case. – tau Jan 26 '22 at 18:45
  • 2
    If you define your functions inside the body of the `useEffect` function, you shouldn't need to store them in a `useCallback`, otherwise you have the correct implementation pattern – Michael Abeln Jan 26 '22 at 18:46
  • 1
    Either that or use a hook like `const useMount = (fn) => { useEffect(fn, []); };` – windowsill Jan 26 '22 at 18:48
  • @CasperKuethe while refs are most often used to store references to React component, they can be used for other purposes. For example, here a member of React team uses a ref to store a callback function when writing a custom hook: https://overreacted.io/making-setinterval-declarative-with-react-hooks/ . In general, refs solve the problem of stale state in callbacks/closures, this problem arises in multiple places when writing cutom hooks – sesm Dec 11 '22 at 15:56

1 Answers1

0

The idiomatic way to write your example would be similar to this:

Note the importance of removing the event listener in the effect cleanup function.

TS Playground

import {useEffect, useRef} from 'react';

const Example = () => {
  const someRef = useRef(0);

  useEffect(() => {
    const handleMouseMove = (event) => { someRef.current = event.clientY; };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, [someRef]);

  return null;
};

If you prefer defining the function outside the effect hook, you'll need useCallback:

import {useCallback, useEffect, useRef} from 'react';

const Example = () => {
  const someRef = useRef(0);

  const updateRefOnMouseMove = useCallback(() => {
    const handleMouseMove = (event) => { someRef.current = event.clientY; };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, [someRef]);

  useEffect(updateRefOnMouseMove, [updateRefOnMouseMove]);

  return null;
};
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • it would be optional to pass in `someRef` as a dependency, right? – tau Jan 26 '22 at 20:29
  • 1
    Correct: It won’t have a behavioral effect to exclude it (but that ultimately depends on your specific linter and configuration for diagnostics). – jsejcksn Jan 26 '22 at 20:31