I am not sure if this is a valid warning by using useEffect around the dependency array, it seems like whenever variable, method or dispatch inside the useEffect are giving warning that
React Hook useEffect has missing dependencies: 'active', 'retrieveUser', and 'dispatch'. Either include them or remove the dependency array of following example, if I just leave it as blank array to perform the componentDidMount functionality
useEffect(() => {
setActive(active);
await retrieveUser(param1, param2);
dispatch(someAction);
}, []). // warning: React Hook useEffect has missing dependencies: 'active', 'retrieveUser', 'param1', 'param2', and 'dispatch'. Either include them or remove the dependency array, but here I just want to perform componentDidMount concept so my dependency list has to be empty
or
useEffect(() => {
await retrieveUser(param1, param2);
dispatch(someAction);
}, [userId]). // warning: React Hook useEffect has missing dependencies: 'retrieveUser', 'param1', 'param2', and 'dispatch'. Either include them or remove the dependency array
Are those valid warning? Especially that I just want to monitor on specific data field, what's the point of adding all inside dispatch or method into the dependency array if I can't add anything into the dependency list(for componentDidMOunt) or I just want to monitor userId, not param1, param2, etc
React is giving this warning because you are using part of your component's state or props in useEffect but have told it not to run the useEffect callback when that state/props changes. React assumes you will want to run that useEffect() whenever the referenced states change so it is giving you a warning. If you were only referencing variables outside of state or props, you would not get this warning. See below for info about conditionally running useEffect.
Since your goal is to only run useEffect only sometimes, you're likely going to need to restructure your component so that different data is in or out of state. It's difficult to recommend more specific solutions without knowing more about the component; but, a good goal is to have the state you utilize in useEffect match up with the changes that you want to trigger a rerender.
There might be other issues with state in your component. For example, I notice that you call setActive(active);. If active is part of the component's state, that means you are setting active to itself. Which isn't necessary. (this is assuming you are following typical naming patterns and likely have a line at the top of your component like:
const [active, setActive] = useState(initialValue);
Conditionally running useEffect
When you provide a second argument to useEffect(), it limits the situations when useEffect runs the callback function on rerenders. This is for performance reasons as explained in the React docs.
In the first useEffect where you provide an empty array, you are telling React to only run those actions (setActive(active)...etc.) when the component mounts and unmounts.
In the second useEffect where you provide an array with userId, you are telling React to only run the callback on mount, unmount, and whenever userId changes.
React is giving you that warning because it knows it won't run useEffect if active...etc or other values change. This can cause different parts of your component to be referencing different states. For example, one part of your component might be using userId = 1 and another part is using userId = 2. This is not how React is designed to work.
Create a hook that executes only once:
const useEffectOnce = effect => {
useEffect(effect, []);
};
use this hook inside your component
useEffectOnce(() => {
const request = async () => {
const result = await retrieveSum(param1, param2);
setResult(result);
};
request();
});
Now, even if your props (param1, param2) change, the effect is called only once. Be very careful when doing this, the component is probably buggy unless your initial props are truly special. I don't see how this is different from disabling the rule with
// eslint-disable-next-line react-hooks/exhaustive-deps
Demo: https://codesandbox.io/s/trusting-satoshi-fngrw?file=/src/App.js
Related
I'm making a checkbox component for my project, and I have it set so that whenever is isChecked state changes, useEffect will run a returnFunction (defined in the props), like so:
export function JCheckbox({ returnFunction }: JCheckboxProps) {
const [isChecked, setIsChecked] = React.useState(defaultState)
React.useEffect(() => {
returnFunction(isChecked)
}, [isChecked])
When I do this, my compiler wants me to include returnFunction in the useEffect dependency array. I'm confused why I would need to track that, it's not used at all anywhere else in the function, and it doesn't change either. The code works perfectly fine without it.
You should add your function (returnFunction) to useEffect dependency because your function reference (returnFunction) may be changed, So react ask you to add your function (returnFunction) in useEffect dependency to recognize that and update them by new function reference.
returnFunction is certainly used as it is called inside the effect, and it could certainly change as the parent component could pass in different props, in which case your component would continue to call the previously passed in function. So the linter is correct, returnFunction is a dependency. However adding it makes it slightly more difficult to detect changes of isChecked, as changing returnFunction would also trigger the effect. Thus it seems way more elegant to call returnFunction from within setIsChecked:
const [isChecked, _setIsChecked] = useState(false);
function setIsChecked(value: boolean) {
returnFunction(value);
_setIsChecked(value);
}
However in the case given it seems that the state should rather be maintained by the parent component, consider lifting it up.
returnFunction is read from props so you probably must include it in the array of dependencies.
You should almost always include every variable and function you declare inside React and use in useEffect callback in the array of dependencies.
Eslint does not expect you to include setState functions and refs that you create in the same component that you execute useEffect.
Edit:
If you have a handleChange event handler and isChecked is toggled whenever the onChange method is invoked, you probably do not need useEffect. Call returnFunction function with the new isChecked value.
I have a useCallback() method below to improve the performance. This will be same logic with useEffect()
If I have a dependency which is router.asPath, but sometimes the router is null, which may cause the function crash.
In order to improvement performance, I do not want to put the whole object of router, as other fields changes, I do not want rerun the function.
Any suggest?
const recordProduct = useCallback(() => {
dispatch(setNextCollectionScroll(router.asPath, hit.handle))
}, [dispatch, hit.handle, router])
The ideal dependency: [dispatch, hit.handle, router.asPath]
But I have to do it now: due to the router object may be null: [dispatch, hit.handle, router]
It is not clear what router are you using, and if you should look elsewhere, but in your simple case of checking dependencies in both useEffect and useCallback you can use Optional chaining from JS.
const recordProduct = useCallback(() => {
dispatch(setNextCollectionScroll(router.asPath, hit.handle))
}, [dispatch, hit.handle, router?.asPath])
When router is null, ? will skip trying to get asPath and you will not get a runtime error.
SIDENOTE
In my opinion you should always use a specific property in an object that you are depending on, especially if you are using a third-party library, as it may happen that they return same object reference with a different value (this should be labeled as badly written library, but just in case depend on properties).
It is easier to follow the flow of data and why a particular dependency hook fires.
// Fetch Products
const fetchProducts = async () => {
const { data } = await axios.get('/api/products')
setProducts(data)
}
// Retrieve all products at reload
useEffect(()=>{
fetchProducts()
},[])`
This is a simple function for retrieving products whenever the home page component loads. So every time it loads it should run fetchProducts(). I understood that an empty array means nothing/no dependency so when the component mounts it will run this function.
The error I receive is "Line 19:7: React Hook useEffect has a missing dependency: 'fetchProduct'. Either include it or remove the dependency array"
I'm new to React and am not really understanding the statement.
Why would removing the array or adding the function to the dependency change anything?
Isn't the way it's written doing exactly what I would need it to do?
That error is telling you that yow fetchProducts function is been used inside yow hook, if you want to fix that you have two choices .
Add it to the brackets of the hook
useEffect(()=>{},[fetchProducts]);
Or created it inside the hook.
useEffect(()=>{
const fetchProducts () =>{...};
fetchProducts()
},[]);
The way you have it is considered as a side effect
This Warning is basically an eslinting problem as in the dependency array takes primitive type but eslint does not correctly detect dependencies therefore you can fix this by a disable eslint above the lines where there is warning. If you async logic in use Effect put your fetchProducts inside React.useCallback hook and add fetchProducts to use Effect dependency array
Recently I saw a few examples of passing setter functions into hook dependency arrays in my coworkers' React code, and it doesn't look right to me. For example:
const MyComponent = () => {
const [loading, setLoading] = useState(true);
useEffect(() => {
doSomeBigLongNetworkRequest();
setLoading(false);
}, [setLoading, /* other deps */]);
// ...
}
My feeling is that they have misunderstood the purpose of the dependency array, which, as I understand it, is to indicate which pieces of state to monitor so that the hook can fire again when they change, not to simply indicate that the hook needs to use the setLoading function. And since the setLoading function never actually changes, including it in the dependencies does nothing.
Am I correct, or does including the setter in the array make sense somehow? My other thought was that maybe this was just a linter error, since the linter cannot recognize that the function is a setter, and thinks it might change.
I should also add that in the instances I've seen, they've included the setter but not the variable. So in the example above, setLoading, but not loading would be in the dependency array, and the hook does not actually need the value of loading.
Yes, you are right there is no need to include them. Here is quote from docs:
React guarantees that setState function identity is stable and won’t
change on re-renders. This is why it’s safe to omit from the useEffect
or useCallback dependency list.
In general again based on docs the recommendation about dependency array is:
If you use this optimization, make sure the array includes all values
from the component scope (such as props and state) that change over
time and that are used by the effect. Otherwise, your code will
reference stale values from previous renders.
I want to use useEffect to fire 2 functions (which are redux actions creators) - one function when component mounts, and the other one on unmount, like so:
// ...
function MyComp(props) {
function handleSelectItem(itemId) {
const data = props.data.filter(/* some filter logic depending on itemId */);
props.selectItem({ itemId, data });
}
useEffect(() => {
handleSelectItem(1);
return props.deselectItem;
}, []);
}
export default connect(null, {selectItem, deselectItem})(MyComp)
It works, but the react-hooks/exhaustive-deps eslint rule telling me that my useEffect has 2 dependencies which I should include into the dependencies array (useEffect's second argument) or remove the array completely. Second option is not suitable as I don't need these functions to be called on each render. Hence, I need to include them both. There is an article regarding including functions into dependency array. Things described there is pretty much clear to me, but I still confused about the redux action creators in dependencies array and how react will shallowly compare this functions. For me this is a little bit hard to understand. What is the right way to resolve this particular case what is the right way to use redux action creators in useEffect in general?