Difference between React useCallback with pure function declared inside and outside component - reactjs

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.

Related

React useEffect missing dependency for a function?

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.

Do I get any performance gain from useCallback when the function is used as a private method?

React's useCallback hook offers a performance gain when passing the generated (memoized) function to a child component in order to avoid unnecessary re-renders.
Do I get any performance gain when the hook is used as internal ("private") function?
For example:
function Foo({numbers}) {
const dep1 = React.useMemo(() => {......}, [....])
const calc = React.useCallback((a) => a * dep1, [dep1])
return (
<>
{numbers.map((num) => (
<div key={num}>
Num: {num}, Calculated: {calc(num)}
</div>
))}
</>
);
}
or does a simple
const calc = (a) => a * dep1
is the same for that case?
In other words, since useCallback is memoizing the function reference, not the function itself, do I have to use it when not passing it as a prop?
For local usage (inside of the component) useCallback literally doesn't make any improvements. It will be called on each re-render and even taking the fact it will take an original function object from memory, it still will re-create the inline function.
However, useMemo can make sense if it caches a value received from some heavy calculations. It use the memoization principle which is useful for avoiding redundant heavy operations.
P.S.
It worth noting that the only justified use case of wrapping private method with useCallback is if (for any reason) you are going to put this method into useEffect dependencies.

When React functional component re-render, does it reassign assigned values & functions?

If a code like this re-render by useEffect's dependency,
// ...
const Test = () => {
// ...
const value1 = "test1"
const func1 = () => {
// do something1
}
useEffect(() => {
const value2 = "test2"
const func2 = () => {
// do something2
}
}, [sth])
return (
// ...
)
}
does value1 & value2 & func1 & func2 reassign to memory?
I'm curious about it, related to optimizing.
Short answer, yes. Every time the function runs the old values will be garbage-collected, new primitive values will be assigned in memory and new references will be created to functions and objects.
But the real question with a "not-that-short" answer is "does it impact performance in a significant way?". And the answer is... depends. In most cases, it will not (see the doc about it). But there are scenarios where you will need to make some changes to have a performance optimization with useCallback and use useMemo.
It's also worth mentioning (as said in Shivam Jha's answer) that a trigger in useEffect no necessarily causes a re-render (DOM paint) because this process happens first on the virtual DOM and will only be persisted in the real DOM when necessary.
I will leave here some other references about this discussion.
Dan Abramov's tweet on memoizing everything (also look at responses)
Kent C. Dodds's article about render performance
Felix Gerschau's article about when render occurs
does value1 & value2 & func1 & func2 reassign to memory?
in short the answer is yes.
it's more clear if you look at it for what it is: Test is a function, so everytime that function is called all the variables inside the function scope (the curly brackets) are re-declared and re-assigned.
Let's dive into the details:
value1 and func1 are in the function's body so they get declared and assigned every time the function is called, they are not related at all, they have just the same name.
value2 and func2 instead are declared inside an useEffect hook with a declared dependency (sth), this means that these 2 variables are redeclared and reassigned only after the first render and after every other render if the sth variable changed its value compared to the previous render.
if you want to optimize value1 so it doesn't change at every render you can use the useMemo hook this way:
const value1 = React.useMemo(() => {
return "test1"; //here you might have a more complicate way to determine value1
}, []); //array of dependencies like for `useEffect`, so `value1` will be recalculated only if any of the values provided in here change. by leaving it empty value1 will always be the **same** variable
you can do similar optimizations with functions too with the useCallback hook
According to docs:
What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.
Also, it does not re-renders the code but runs the code again when the dependencies passed to it changes
Tip: Optimizing Performance by Skipping Effects describes solving performance problem due tocleaning up or applying the effect after every render.
Also, you can free up allocated memory (if not freed automatically) or run some side effects after running the code (setTimeOut, etc) by using useEffect with cleanup.
Basically do everything you want to run after useEffect inside a return function:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});

Need to wrap every useMemo dependency in a useCallback

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 !

Are parameters needed for inner functions to access props + variables of a React Functional Components

I have a functional component that has a couple of functions inside of it. In the example below, the inner functions are function1 and function2. I am trying to figure out if i need to explicitly pass the functional component's props (prop1, prop2), as well as other declared variables within the functional component (variable1, variable2) into these inner functions. Here is an example:
import React from 'react'
import * as d3 from 'd3';
function MyFunction({prop1, prop2}) {
const variable1 = '#333333';
const variable2 = 'Jimmy';
const function1 = () => {
d3.select('g.g1')
.append('rect')
.attr('color', variable1)
}
const function2 = () => {
d3.select('g.g2')
.append('text')
.text(prop1.label)
}
return (
<svg>
<g className='g1' />
<g className='g2' />
</svg>
);
}
export default MyFunction;
It seems to simplify my code if I don't have to explicitly declare parameters for function1 and function2 to be able to use these props + other variables declared in the upper scope, however I am not sure if this is a bad practice, or if this will cause other issues with my code. Is this okay?
Edit
Wanted to note that this does, in fact, seem to work without any issues. My question is more a matter of - should I do this / is it okay / are there problems I may run into down the road doing this?
Using a function in a function is perfectly fine. There are all kinds of articles you can read on if you should use useCallback. I'll talk about that in a second.
No, you don't need to pass the props to each function. The reason for this is closures and what it captures. Here is some reading on it under closures. Your arrow function closes over the props variable if they reference it. So nope, you don't need to pass it, and check out that link. And Chapter 2 if you really want to dig in depth.
As for performance, it's normally fine! Every time your component renders though, function1 and function2 will be redefined. They'll be the same exact function (unless d3 changes), but with a different reference, so the old function1 !== the new function1 even if they do the same thing. This is why that matters...
For a little more overhead though (per render of the component, possibly saving many renders of other components or useEffect executions), you can have react analyze if it should redefine function1 or function2 and if not, it keeps the same reference. This is what useCallback does. Honestly, I use useCallback if I'm ever unsure if the function will cause me issues. An example is when a child component has function1 passed in as a prop. That prop will ALWAYS be evaluated as a new value on every render of the parent because it's a new reference, unless you use useCallback.
You do want to pass the props as explicit arguments. As long as you don't pass any props that you're not using (prop2 above, for example), your code will not be simpler from omitting them. Especially as your use cases for them grows, along with the functions that need them, this will actually prevent verbose code.
But, as you found yourself, it is not required for a working component. The react docs show this throughout all of their examples. For ex - visit this link.
first of all this approach of having function inside a function is not recommended , it proves performance expenseive very often and yes if you have to use you need to pass the props into them. Also see this link here

Resources