I guess it should come as no surprise that there are already so many questions that speak to this, however, each use case depends heavily on context so it is difficult to apply what others have experienced in attempting to solve my own issue.
Despite having tread ever so carefully in an attempt to write components that only re-render when absolutely necessary in a large React application with dozens of components and complex state, i have found it to be practically impossible to control causing unintended execution of the useEffect hook in several of my components.
That is to say, unless i remove a useEffect's dependencies when this becomes an issue, however, React's documentation does not explicitly state whether or not including all of a useEffect's dependencies is absolutely required.
The only hint that speaks to the fact that all of a useEffect's dependencies must be included is the fact that my IDE complains if i attempt to exclude one or all of them in-order to prevent a useEffect from executing when it should not or in-order to enforce a single execution.
This is a never ending rabbit hole that is costing me dozens of hours of backtracking and troubleshooting, and so id like to propose the following question in an attempt to get a definitive answer once and for all:
Is it ever wrong to exclude dependencies from a useEffect hook as a means of preventing it from executing more than once?
Quite frankly, this has been beyond frustrating and all of this time wasted has caused me to seriously consider ditching React altogether, despite its popularity, for Angular. For what it is worth i have never run into issues like this at all in Angular. The component life cycle in Angular is clear and simple to control.
UPDATE in response to Nathan below, i'll share one VERY simple example of a useEffect hook in my application and my approach to controlling its execution without removing it's dependencies.
Some context. authStatus
and uiStatus
are references to state in a React Redux store.
The hook before i took control over its execution:
...
const authStatus = useSelector((state:any) => state.auth);
const uiStatus = useSelector((state:any) => state.interface);
const [frontCameraEnabled, setFrontCameraEnabled] = useState<boolean>(false);
...
useEffect(() => {
if(authStatus.isAuthenticated) {
switch(uiStatus.device.operatingSystem) {
case 'ios':
setFrontCameraEnabled(true);
break;
}
dispatch(cameraCheckPermissions());
}
}, [dispatch, authStatus.isAuthenticated, uiStatus.device.operatingSystem]);
Now — immediately I am well aware of the fact that updating the frontCameraEnabled
state will cause my component to re-render, and so to prevent this logic from executing a second time when useEffect executes again as a result, i do the following which seems insane to me to have to take this kind of approach in dozens of places throughout my application:
OUTSIDE of my component i define a boolean:
let initalChecksComplete:boolean = false;
and then INSIDE my useEffect hook:
useEffect(() => {
if(!initalChecksComplete && authStatus.isAuthenticated) {
initalChecksComplete = true;
switch(uiStatus.device.operatingSystem) {
case 'ios':
setFrontCameraEnabled(true);
break;
}
dispatch(cameraCheckPermissions());
}
}, [dispatch, authStatus.isAuthenticated, uiStatus.device.operatingSystem]);
This just feels wrong especially in places where the logic is actually complex. I feel like if the useEffect hook is designed to execute more than once to begin with then the React team should also provide a hook that is designed to execute ONCE and ONLY once when a component initially mounts/renders.