Your both dispatch are called after first render so even before your second render value is 0 so your second useEffect won't be able detect change as there is no change.
Let's see what is happening in your render method
First Render:
a = 0
first useEffect: dispatch({ a : 1 })
second useEffect: dispatch({ a : 0 })
so now in your redux store a is 0.
Second Render
a = 0
first useEffect: doesn't run as there is no dependency
second useEffect: doesn't run as a hasn't changed.
Answer from Amit Chauhan on Stack OverflowuseEffect not working when dependency value changed
reactjs - How to fix missing dependency warning when using useEffect React Hook - Stack Overflow
Bug: Clean up useEffect given [] empty dependency array causes a tedious linter error
What is the purpose of useEffect without a dependency array?
I'm guessing the only difference is that useEffect will guarantee that it will always run after the render, it also makes sure that every other component that needs to re-render will finish rendering before running the effect.
Though I can't really think of a good use case for this.
More on reddit.comVideos
Your both dispatch are called after first render so even before your second render value is 0 so your second useEffect won't be able detect change as there is no change.
Let's see what is happening in your render method
First Render:
a = 0
first useEffect: dispatch({ a : 1 })
second useEffect: dispatch({ a : 0 })
so now in your redux store a is 0.
Second Render
a = 0
first useEffect: doesn't run as there is no dependency
second useEffect: doesn't run as a hasn't changed.
PLEASE, stop using
useSelector(state => state.mainReducer);
it doesn't make any sense
there should be a simple state transformation (subselection)
const a = useSelector(state => state.a)
taken directly from redux docs:
const counter = useSelector(state => state.counter)
update
you can see effect (from store change) with slightly changed component
function MyComponent(props) {
const a = useSelector(state => state.a);
const dispatch = useDispatch();
console.log('render: ', a);
useEffect(() => {
console.log('use effect: ', a);
dispatch({ type: 'A', payload: a });
}, [a])
useEffect(() => {
console.log('did mount: ', a);
dispatch({ type: 'A', payload: 1 })
}, []);
return (<View style={styles.container}>
<Text style={styles.text}>{a}</Text>
</View>);
};
It should result in log:
render: 0// initial stateuse effect: 0// first effect run- // dispatch
0... processed in store by reducer but results in the same state ...
// ... and in our rendering process we still working on an 'old'areaded from state on the beginning of render did mount: 0// 'old'a
// dispatch1... changed state in redux store.... rendered text
0
...
...//
useSelectorforces rerendering - change detectedrender: 1// latest dispatched value, processed by reducers into new state, rereaded by selectoruse effect: 1//useEffectworks AS EXPECTED as an effect ofachange- .... rendered text
1
...
...
- no more rerenderings - latest dispach not changed state
Of course dispatch from other component will force update in this component ... if value will be different.
If you aren't using fetchBusinesses method anywhere apart from the effect, you could simply move it into the effect and avoid the warning
useEffect(() => {
const fetchBusinesses = () => {
return fetch("theURL", {method: "GET"}
)
.then(res => normalizeResponseErrors(res))
.then(res => {
return res.json();
})
.then(rcvdBusinesses => {
// some stuff
})
.catch(err => {
// some error handling
});
};
fetchBusinesses();
}, []);
If however you are using fetchBusinesses outside of the effect, you must note two things
- Is there any issue with you not passing
fetchBusinessesas a method when it's used during mount with its enclosing closure? - Does your method depend on some variables which it receives from its enclosing closure? This is not the case for you.
- On every render, fetchBusinesses will be re-created and hence passing it to useEffect will cause issues. So first you must memoize fetchBusinesses if you were to pass it to the dependency array.
To sum it up I would say that if you are using fetchBusinesses outside of useEffect you can disable the rule using // eslint-disable-next-line react-hooks/exhaustive-deps otherwise you can move the method inside of useEffect
To disable the rule you would write it like
useEffect(() => {
// other code
...
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
There are very good options for state management librariess if you are creating a new app or have enough flexibility. Check out Recoil.
Just for completeness:
1. (Stopped working) Use function as useEffect callback
useEffect(fetchBusinesses, [])
2. Declare function inside useEffect()
useEffect(() => {
function fetchBusinesses() {
...
}
fetchBusinesses()
}, [])
3. Memoize with useCallback()
In this case, if you have dependencies in your function, you will have to include them in the useCallback dependencies array and this will trigger the useEffect again if the function's params change. Besides, it is a lot of boilerplate... So just pass the function directly to useEffect as in 1. useEffect(fetchBusinesses, []).
const fetchBusinesses = useCallback(() => {
...
}, [])
useEffect(() => {
fetchBusinesses()
}, [fetchBusinesses])
4. Function's default argument
As suggested by Behnam Azimi
It's not best practice, but it could be useful in some cases.
useEffect((fetchBusinesses = fetchBusinesses) => {
fetchBusinesses();
}, []);
5. Create a custom hook
Create a custom hook and call it when you need to run function only once. It may be cleaner. You can also return a callback to reset re-run the "initialization" when need.
// customHooks.js
const useInit = (callback, ...args) => {
const [mounted, setMounted] = useState(false)
const resetInit = () => setMounted(false)
useEffect(() => {
if(!mounted) {
setMounted(true);
callback(...args);
}
},[mounted, callback]);
return [resetInit]
}
// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
const [resetInit] = useInit(fetchBusiness, arg1, arg2)
useEffect(() => {
resetInit()
}, [requiresRefetch, resetInit]);
6. Disable eslint's warning
Disabling warnings should be your last resort, but when you do, better do it inline and explicitly, because future developers may be confused or create unexpected bugs without knowing linting is off
useEffect(() => {
fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
I use useEffect all the time in my applications, but I've never understood the difference between having code inside a useEffect with no dependency array, and just running it in the component itself.
From the documentation. What is the difference between this
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
}and this?
function Example() {
const [count, setCount] = useState(0);
document.title = `You clicked ${count} times`;
}wont both run on every render?
Edit: thanks for all the helpful responses. It seems like my intuition was almost right. It does behave slightly differently, but almost never in a way you want. However it would be good for protecting DOM manipulating functions for SSR.
I'm guessing the only difference is that useEffect will guarantee that it will always run after the render, it also makes sure that every other component that needs to re-render will finish rendering before running the effect.
Though I can't really think of a good use case for this.
more or less the same, but you wouldn't do either of these things. you'd use the useEffect with a dependency array that has count.
otherwise you're doing unnecessary work. say this Example components' parent rerenders which causes Example to rerender, now we're changing the document title even though count never changed.
I would recommend writing this as follows:
const previousFooRef = useRef(props.foo);
useEffect(() => {
if (previousFooRef.current !== props.foo) {
animateSomething(ref, props.onAnimationComplete);
previousFooRef.current = props.foo;
}
}, [props.foo, props.onAnimationComplete]);
You can't avoid the complexity of having a condition inside the effect, because without it you will run your animation on mount rather than just when props.foo changes. The condition also allows you to avoid animating when things other than props.foo change.
By including props.onAnimationComplete in the dependencies array, you avoid disabling the lint rule which helps ensure that you don’t introduce future bugs related to missing dependencies.
Here's a working example:
Suppress the linter because it gives you a bad advice. React requires you to pass to the second argument the values which (and only which) changes must trigger an effect fire.
useEffect(() => {
animateSomething(ref, props.onAnimationComplete);
}, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps
It leads to the same result as the Ryan's solution.
I see no problems with violating this linter rule. In contrast to useCallback and useMemo, it won't lead to errors in common case. The content of the second argument is a high level logic.
You may even want to call an effect when an extraneous value changes:
useEffect(() => {
alert(`Hi ${props.name}, your score is changed`);
}, [props.score]);
If you use inside your useEffect some functions/variables that are declared outside of the useEffect , then you would have to add it as a dependency in case it might change.
External variables that are used inside of useEffect should be inculded in the dependency array, and your functions are defined outside of it, so that's the reason it's warning you.
Though there are some exceptional cases when you don't have to add everything.
For example, if you call setState method inside useEffect, it doesn't have to be inculded in the dependency array (because React will make sure this function won't change).
Read more about what to add to the dependencies here: https://devtrium.com/posts/dependency-arrays
If your functions or methods are going to be called only for one time then it's better to declare and invoke the function in the same useEffect hook.
For Example:-
useEffect(() => {
const options = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};
const getCurrentDate = () => {
let date = new Date();
setDate(date.toLocaleDateString("en-us", options));
};
getCurrentDate();
}, []);
As the above code shows try to include the functions or methods inside your useEffect hook or else if you want to try to add some dependency which can be variable that when that changes your useEffect will trigger again.
Hi! I was coding and opened my terminal to find I had a warning:
React Hook useEffect has a missing dependency: 'getEmergencyTasks'. Either include it or remove the dependency array.
Here's the code:
const [currentEmergencyId, setCurrentEmergencyId] = useState(0);
const [tasks, setTasks] = useState([]);
const getEmergencyTasks = async () => {
try {
const response = await axios.get(`emergency/${currentEmergencyId}/tasks`);
console.log(response.data);
setTasks(response.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getEmergencyTasks();
}, [currentEmergencyId])I don't know why I should include getEmergencyTasks function to dependency array of useEffect. The UI is working ok fetching the data when using the setCurrentEmergencyId function to mutate the currentEmergencyId variable. COuld anyone help me? Thank you!