I'm trying to make a simple example that follows the React Hooks example in the doc at page https://reactjs.org/docs/hooks-reference.html#usecallback
Without useCallback
the code works find as is in this example:
import React, { useCallback } from "react";
function Test(props) {
function doSomething(a, b) {
console.log("doSomething called");
return a + b;
}
return (
<div>
{Array.from({ length: 3 }).map(() => (
<div>{doSomething('aaa','bbb')}</div>
))}
</div>
);
}
export default Test;
However, when I add what I think is the right code for useCallback
as follows, I get an error (a is not defined)
import React, { useCallback } from "react";
function Test(props) {
function doSomething(a, b) {
console.log("doSomething called");
return a + b;
}
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b]
);
return (
<div>
{Array.from({ length: 3 }).map(() => (
<div>{memoizedCallback("aaa", "bbb")}</div>
))}
</div>
);
}
export default Test;
The problem code is here:
The intent of useCallback
is to be able to leverage props or state that are in the current scope and that may change on re-render. The dependency-array then tells React when you need a new version of the callback. If you are trying to memoize an expensive computation, you need to use useMemo
instead.
The example below demonstrates the differences between useCallback
and useMemo
and the consequences of not using them. In this example I am using React.memo
to prevent Child
from re-rendering unless its props or state change. This allows seeing the benefits of useCallback
. Now if Child
receives a new onClick
prop it will cause a re-render.
Child 1 is receiving a non-memoized onClick
callback, so whenever the parent component re-renders, Child 1 always receives a new onClick
function so it is forced to re-render.
Child 2 is using a memoized onClick
callback returned from useCallback
and Child 3 is using the equivalent via useMemo
to demonstrate the meaning of
useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)
For Child 2 and 3 the callback still gets executed every time you click on Child 2 or 3, useCallback
just ensures that the same version of the onClick
function is passed when the dependencies haven't changed.
The following part of the display helps point out what is happening:
nonMemoizedCallback === memoizedCallback: false|true
Separately, I'm displaying somethingExpensiveBasedOnA
and a memoized version using useMemo
. For demonstration purposes I'm using an incorrect dependency array (I intentionally left out b
) so that you can see that the memoized version doesn't change when b
changes, but it does change when a
changes. The non-memoized version changes whenever a
or b
change.
import ReactDOM from "react-dom";
import React, {
useRef,
useMemo,
useEffect,
useState,
useCallback
} from "react";
const Child = React.memo(({ onClick, suffix }) => {
const numRendersRef = useRef(1);
useEffect(() => {
numRendersRef.current++;
});
return (
<div onClick={() => onClick(suffix)}>
Click Me to log a and {suffix} and change b. Number of Renders:{" "}
{numRendersRef.current}
</div>
);
});
function App(props) {
const [a, setA] = useState("aaa");
const [b, setB] = useState("bbb");
const computeSomethingExpensiveBasedOnA = () => {
console.log("recomputing expensive thing", a);
return a + b;
};
const somethingExpensiveBasedOnA = computeSomethingExpensiveBasedOnA();
const memoizedSomethingExpensiveBasedOnA = useMemo(
() => computeSomethingExpensiveBasedOnA(),
[a]
);
const nonMemoizedCallback = suffix => {
console.log(a + suffix);
setB(prev => prev + "b");
};
const memoizedCallback = useCallback(nonMemoizedCallback, [a]);
const memoizedCallbackUsingMemo = useMemo(() => nonMemoizedCallback, [a]);
return (
<div>
A: {a}
<br />
B: {b}
<br />
nonMemoizedCallback === memoizedCallback:{" "}
{String(nonMemoizedCallback === memoizedCallback)}
<br />
somethingExpensiveBasedOnA: {somethingExpensiveBasedOnA}
<br />
memoizedSomethingExpensiveBasedOnA: {memoizedSomethingExpensiveBasedOnA}
<br />
<br />
<div onClick={() => setA(a + "a")}>Click Me to change a</div>
<br />
<Child onClick={nonMemoizedCallback} suffix="1" />
<Child onClick={memoizedCallback} suffix="2" />
<Child onClick={memoizedCallbackUsingMemo} suffix="3" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here's a related answer: React Hooks useCallback causes child to re-render
You have extra { }
after =>
thus creating a block and scope, simply remove them and ;
too as it makes it a statement. Other wise pass the arguments to it, use this
const memoizedCallback = useCallback(
() => doSomething(a, b)
,
[a, b]
);
It says undefined
because the scope of => { }
doesn't receive a,b
. I hope it helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.