useEffect(() => {
calculateTip();
}, [bill, tipPercentage, numberOfPeople]);
const calculateTip = () => {
const tip = ((tipPercentage / 100) * bill).toFixed(2);
const tipPerGroup = ((tipPercentage / 100) * bill * numberOfPeople).toFixed(
2
);
setTipPerGroup(tipPerGroup);
setTip(tip);
};
I get an error:
React Hook useEffect has a missing dependency: 'calculateTip'. Either include it or remove the dependency array
Why does useEffect need to have a function in its dependency array? I mean the function never changes, it is the same same function, why does React force me to write it inside the dependency array?
Why does useEffect need to have a function in its dependency array. I
mean the function never changes . It is the same same function why
does react force me to write it inside the dependency array?
If that function is defined inside the component then it is not the same function, on the contrary it is re-created on each render. But that is not much related to the reason for the warning.
The reason why that warning asks you to put the function as dependency is the same as for other variables. The function itself may be referencing some variables which may become stale.
Related
I am trying useCallback for first time. I am not able to see any changes from earlier output in terms of function re rendering.
My function structure will be
const myfunction = ( array1, array2) => {
//doing a very big calcuation
}
what I tried to use is
const getCards = useCallback((a,b) => myfunction(a,b),[myfunction])
I tried to to pass a,b in dependencies of Usecallback which gives me parameters not found error.
I am passing getCards into the component like this
<Component summaryCallback={getCards}>
I am trying to reduce the number of call inside my function. I want to call this function only if there is a change in array1 or array2 from my previous rendering
Thanks in advance
All hooks are stored with their respective dependencies. If you use a variable that's not included as a dependency to a hook, their initial value is used.
Adding it to the dependency list will enforce the update of the hook and therefore all data inside the hook.
Either way, you might not at all use useCallback if you've got large/heavy computations. You might consider useMemo for it. Ref: https://reactjs.org/docs/hooks-reference.html#usememo
You might use it like the following:
const cards = useMemo(() => { /* heavy computation */ }, [depA, depB]);
return <CardList cards={cards} />
It rerenders everythime, you're dependencies depA or depB change.
Will there be a difference between these two approaches?:
// outside component
const magicIdx = (i) => (i - 1) / 3
//inside component
const calcIdxOut = useCallback(magicIdx, [])
const calcIdxIn = useCallback((i) => (i - 1) / 3, [])
Does defining a pure function outside the component and using it in a useCallback without dependencies makes any difference?
There's no point in using useCallback with a function declared outside of the component. The purpose of useCallback is to give referential stability to a function, even though that function is technically redefined every render. Since that only applies to functions defined inside the component, it does nothing useful here.
// Stable reference - only declared once
function example() {}
function App() {
// Unstable reference - redeclared each time App renders
function example2() {}
// Stable reference - even though example2 changes,
// example3 reference only changes if dependencies to useCallback change.
const example3 = useCallback(example2, []);
}
So if the above examples make sense, you'll notice that example is already stable, so there's no reason to ever use useCallback on it.
Your function is not expensive, so unless the purpose here is to prevent a memoized child component from re-rendering, referential stability really isn't a large concern anyway.
I hope someone can help me. I tried to find the answer to my question but I couldn't, so if it is there and I couldn't find it, I apologize in advance.
So, I have an expensive operation that depends on 3 objects stored in the redux store. Since it is expensive, I want to execute it only when any of those 3 objects change.
To avoid making the function executed by useMemo too complex I split it in smaller functions that are then call when needed, something like this:
const computedValue = useMemo(() => {
...
const result = processStoreObject1(storeObject1)
...
}, [storeObject1, storeObject2, storeObject3, processStoreObject1])
Now, I do not want to list processStoreObject1 as a dependency of useMemo, the computed value does not depend on it, the computed value only depend on the 3 store object. However, if I do not list the function as a dependency of useMemo I get this lint warning:
"React Hook useMemo has a missing dependency: 'processStoreObject1'. Either include it or remove the dependency array. eslint(react-hooks/exhaustive-deps)"
Because of this warning I have to include the function in the dependencies array, and because the function is declared inside the component, similar to this:
const MyComponent = () => {
...
const processStoreObject1 = () => {
// Do something
}
...
}
I have to wrap it in a useCallback, otherwise it changes with each render and the useMemo is recalculated all the time (which is wrong). The warning that I get if I do not wrap the processStoreObject1 with useCallback is this:
"The 'processStoreObject1' function makes the dependencies of useMemo Hook (at line NNN) change on every render. To fix this, wrap the 'processStoreObject1' definition into its own useCallback() Hook.eslint(react-hooks/exhaustive-deps)"
I know one easy solution is to define processStoreObject1 outside the component so it does not get created in each render but I do not like that way, the function is only consumed inside the component so I want to have the definition in there.
To summarize, the question is, how can I use a function inside the function executed by useMemo without adding the dependency to the dependencies array. I know it is doable, I saw some examples of functions being used without being in the dependencies array.
I will be thankful if anyone can help me.
Thanks !
I'm new to Hooks and have been encountering some cases that have been sending me chasing my tail.
Hope someone can explain or provide solutions that make sense to me:
Loading a method only once on mount of a component is confusing. I tried this method, it works, but don't understand it. Can someone explain this to me:
const useMountEffect = fun => useEffect(fun, [])
useMountEffect(() => {
if (!isEdit) handleAddRow()
})
const handleAddRow = () => {
let appendArr = [...listArr, '']
setListArr(appendArr)
}
Following this thread:
How to call loading function with React useEffect only once
I tried just using useEffect without dependency and eslint does not like that and they recommend adding a skip next line which seems kind of hacky:
// eslint-disable-next-line
If I'm correct you want something similar to componentDidMount life-cycle method.
The way to do that is
function MyComponent(props){
useEffect(()=>{
// do stuff here...
}, []) // <-- empty dependency array
return <div></div>
}
To understand what's happening you'll need to understand how the useEffect hooks work.
Using the useEffect hook without any dependencies will trigger the effect every time some state or prop changes and causes a re-render.
but if we pass an empty array as a dependency it will be as the effect not dependent on anything else, so it will only trigger when the component mounts.
The empty array [] argument tells useEffect() to run only once
It will run only once on mount, like componentDidMount used to
Explanation:
useEffect() takes 2 arguments - your function and an array of dependencies
useEffect(
yourFunction, // <- function that will run on every dependency update
[] // <-- empty dependency array
)
The dependency array argument tells it when to run your function. If the dependencies don't change it won't run again. If dependencies do update it will run your function again.
If you pass in an empty array (no dependencies) then the function will only run on mount.
ESLint missing dependency error
React Hook useEffect has a missing dependency: 'xxx'. Either include it or remove the dependency array react-hooks/exhaustive-deps
The reason for the ESLint rule is that useEffect() is designed to run on every dependency update it will expect you to add all the dependencies to that array.
To ignore this you can add above the dependency array line:
// eslint-disable-next-line react-hooks/exhaustive-deps
For anyone looking to execute a function once per whole App (globally), this could be a solution:
if (typeof window !== 'undefined') { // Check if we're running in the browser.
// ✅ Only runs once per app load
checkAuthToken();
loadDataFromLocalStorage();
}
function App() {
// ...
}
Source / extended explanation here.
I have a useEffect hook in my component which call a Redux action to fetch some data.
useEffect(
() => {
props.getClientSummaryAction();
},[]
);
When I go to save the file the linter does this.
useEffect(
() => {
props.getClientSummaryAction();
}, [props]
);
Which obviously send my component into an infinite loop as getClientSummaryAction fetches some data which updates the props.
I have used deconstruction like this and the linter updates the array.
const { getClientSummaryAction } = props;
useEffect(
() => {
getClientSummaryAction();
},
[getClientSummaryAction]
);
Which I am fine with but it doesn't really make sense because getClientSummaryAction will obviously never be updated, its a function.
I just want to know why the linter is doing this and whether this is best practice.
It's not unwanted. You know for a fact that the dependency is not going to change, but React can possibly know that. The rule is pretty clear:
Either pass an array containing all dependencies or don't pass anything to the second argument and let React keep track of changes.
Pass the second argument to useEffect is to tell React that you are in charge of hook's call. So when you don't pass a dependency that is included inside your effect eslint will see this as a possible memory leak and will warn you. DO NOT disable the rule just continue to pass the dependency as you are already doing. May feel redundant but it's for the best.