24

I have a context that is used to show a full page spinner while my application is performing long running tasks.

When I attempt to access it inside useEffect I get a the react-hooks/exhaustive-deps ESLint message. For example the following code, although it works as expected, states that busyIndicator is a missing dependency:

const busyIndicator = useContext(GlobalBusyIndicatorContext);

useEffect(() => {
    busyIndicator.show('Please wait...');
}, []);

This article suggests that I could wrap the function with useCallback which might look as follows:

const busyIndicator = useContext(GlobalBusyIndicatorContext);
const showBusyIndicator = useCallback(() => busyIndicator.show('Please wait...'), []);

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

Although this works it has moved the issue to the useCallback line which now complains about the missing dependency.

Is it ok to ignore the ESLint message in this scenario or am I missing the something?

Newm
  • 1,313
  • 3
  • 16
  • 29

2 Answers2

43

If your busyIndicator is not changed during the life of the component, you could simply add it as a dependency to useEffect:

const busyIndicator = useContext(GlobalBusyIndicatorContext);

useEffect(() => {
    busyIndicator.show('Please wait...');
}, [busyIndicator]);

If busyIndicator could be changed and you don't want to see an error, then you could use useRef hook:

const busyIndicator = useRef(useContext(GlobalBusyIndicatorContext));

useEffect(() => {
    busyIndicator.current.show('Please wait...');
}, []);

The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class. read more

Andrii Golubenko
  • 4,879
  • 1
  • 22
  • 27
  • 1
    Thanks, I have used useRef and useContext independently but it never occurred to me that they could be used together – Newm May 21 '19 at 15:54
  • @Newm The useRef trick was really great and solved the problem for me... thank you! Two years later, here I am asking if you have enough understanding to explain WHY it fixes the problem? Very curious to understand what this is doing differently. – ogg130 Feb 05 '21 at 02:39
  • 1
    Just found this trick and it worked a treat. @ogg130 The reason that it works is that useRef create a reference that persists for the lifetime of the component - from the docs https://reactjs.org/docs/hooks-reference.html#useref "useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component." Usually you see it used with references to elements, but you can use it for lots of things. – Jarrod McGuire Apr 14 '21 at 22:17
1

No need to Wrap your useContext in useRef Hook. you can update your context data just call in useEffect Brackets like this.

const comingData = useContext(taskData);

useEffect(() => {
console.log("Hi useEffect");
}},[comingData]); //context data is updating here before render the component
azeem
  • 341
  • 3
  • 4
  • 4
    This example won't cause the component to render. If you have anything in the useEffect that will trigger a rerender (e.g. dispatch to store with a selector, update of state), the component rerender will create a new reference to the useContext, hence causing the useEffect to run again, and create an infinite loop. At least that's what happened for me. Using useRef solved it nicely – Jarrod McGuire Apr 14 '21 at 22:12
  • 3
    console.log will trigger everytime when any event will update the context, in my case it added me +800MB of memory in a few seconds – Aziz Jun 26 '21 at 00:42