1

I need to access variables in a useEffect but only trigger the method when SOME of them get updated.

For example:

I want to call useEffect when data changes, but NOT when saveTimeout or saveMethod change.

In the same fashion, I want to call saveMethod when my component dismounts, but I can't because it needs to be in the dependency array, therefore calling it at every change of saveMethod.

function SavingComponent({data, apiInfo}){
    [saveTimeout, setSaveTimeout] = useState(null);

    const saveMethod = useCallBack(()=>{
        clearTimeout(saveTimeout);

        // api call to save the data using apiInfo

    }, [data, saveTimeout, apiInfo])

    // Start a timer to save the data 2 seconds after it is changed (not working)
    useEffect(()=>{
        clearTimeout(saveTimeout);
        setSaveTimeout(setTimeout(saveMethod, 2000));
    }, [data, saveTimeout, saveMethod]);

    // Save immediately on dismount only (not working)
    useEffect(()=>{
      return ()=> { saveMethod(); }
    },[saveMethod])

    return (// some rendering)
}

This is an issue I am constantly running into with other cases and have to hack around. Usually using a custom usePrevious method. I would compare the previous value of the prop to the current and return immediately if the prop I am interested in didn't change.

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

What is the proper way to only call useEffect when SOME dependencies get updated?

Just Mohit
  • 141
  • 1
  • 13
Ryan Pergent
  • 4,432
  • 3
  • 36
  • 78
  • Maybe this is not really related about question, but the way using `clearTimeout` is similar to function [Debounce](https://stackoverflow.com/a/41215941/13107433) , may easier to let you control `useEffect`? – 高鵬翔 May 11 '20 at 09:39
  • I used debounce before switching to hooks. Is it going to be compatible with a useCallBack Method? But yeah, the question is more about how to handle cases like that in general, the timeout is just an example among dozens. – Ryan Pergent May 11 '20 at 09:46
  • I haven't use `debounce` in hook before , but it looks have similar answer using hook under [that one](https://stackoverflow.com/a/58594890/13107433) ? Although it have four upvote, it may need to try to confirm that... – 高鵬翔 May 11 '20 at 09:53

1 Answers1

1

First thing that having variables and functions which are used in useEffect as dependencies is recommended and best practice. This is a very good article written by Dan Abramov which is deep dive in how useEffect works and why you need to follow that best practice.

Now back to your case.

I don't think you want the component re-render when you set saveTimeout, so instead use useState you can useRef for that saveTimeout = useRef(null) so you can remove saveTimeout from dependencies.

If you don't want saveMethod to be in dependencies you can move it inside useEffect so that you can remove it from dependencies too.

function SavingComponent({data, apiInfo}){
    const saveTimeout = useRef(null);

    // move saveMethod inside
    useEffect(()=>{
        const saveMethod = ()=>{
            clearTimeout(saveTimeout.current);
            // api call to save the data using apiInfo
        }
        // saveMethod will be called after 2 seconds
        saveTimeout.current = setTimeout(saveMethod, 2000);
    }, [data]);

    // move saveMethod inside
    useEffect(()=>{
        const saveMethod = ()=>{
            // api call to save the data using apiInfo
        }
        // saveMethod will be call when the component unmounted
        // But make sure you are not update any state inside saveMethod
        return saveMethod
    },[])

    return (// some rendering)
}
Just Mohit
  • 141
  • 1
  • 13
Tony Nguyen
  • 3,298
  • 11
  • 19
  • This article is extremely interesting! It refers to `useReducer` but not `useRef`. Is your general advice to use `useRef` when I need to access a variable that is outside of the scope of my `useEffect` but shouldn't trigger it when changed? – Ryan Pergent Jul 04 '20 at 16:34
  • @RyanPergent In the article it is mentioned that you can move the function inside the `useEffect` so you can remove it from dependency, and there is another way is using `useReducer` as well. `useRef` is another aspect, using it when you want to change the value with change state and re-render the component (the article explains as well) – Tony Nguyen Jul 05 '20 at 00:49
  • Yes, I read the whole thing. Putting the function inside `useEffect` doesn't work when you need to use it in more places, and `useReducer` seems overly complicated. Your suggestion with `useRef` seems both simple and efficient, so that's why I was asking if you recommend it in general (or if it has some sort of caveat). – Ryan Pergent Jul 05 '20 at 16:04
  • In the example that you gave with `saveMethod` called on dismount, you omitted the dependencies. However, my issue is specifically that I have dependencies that stop me from using the `useEffect` for dismount. Do you have an example where dependencies are not omitted? – Ryan Pergent Jul 08 '20 at 09:45
  • The second useEffect did not omit any dependencies, because you create `saveMethod ` inside `useEffect `, the `useEffect ` won't depend on `saveMethod ` method anymore – Tony Nguyen Jul 08 '20 at 09:49
  • Generally, in the complex situation like this, using `useReducer` would be better. – Tony Nguyen Jul 08 '20 at 09:50
  • The saveMethod inside the second useEffect needs to access `data`, so shouldn't that `useEffect` add `data` to its dependency? – Ryan Pergent Jul 08 '20 at 09:59
  • If you need data, The `useReducer ` would definitely be the choice – Tony Nguyen Jul 08 '20 at 10:35
  • Actually after more testing, `useReducer` does not help. I still need to add the state of the reducer to the dependencies, therefore calling the second `useEffect` at every change – Ryan Pergent Jul 08 '20 at 13:22
  • I recommend you read the article a few times to get a better understanding, then apply them correctly in your case. If you till not figure out, please create a new question or update this question with more context. With this context it's the best suggestion I can give – Tony Nguyen Jul 08 '20 at 13:42