Issue
I have gone through a couple of articles on useCallback
and useMemo
on when to use and when not to use but I have mostly seen very contrived
code. I was looking at a code at my company where I have noticed someone have done this:
const takePhoto = useCallback(() => {
launchCamera({ mediaType: "photo", cameraType: "front" }, onPickImage);
}, []);
const pickPhotoFromLibrary = async () => {
launchImageLibrary({ mediaType: "photo" }, onPickImage);
}
const onUploadPress = useCallback(() => {
Alert.alert(
"Upload Photo",
"From where would you like to take your photo?",
[
{ text: "Camera", onPress: () => takePhoto() },
{ text: "Library", onPress: () => pickPhotoFromLibrary() },
]
);
}, [pickPhotoFromLibrary, takePhoto]);
This is how onUploadPress is called:
<TouchableOpacity
style={styles.retakeButton}
onPress={onUploadPress}
>
Do you think this is the correct way of calling it? Based on my understanding from those articles, this looks in-correct. Can someone tell me when to use useCallback
and also maybe explain useCallback
in more human terms?
Article I read: When to useMemo and useCallback.
Solution
useCallback
returns a normal JavaScript function regarding how to use it. It is the same as the one it gets as first parameter regarding what it does. The difference is that this function doesn't get recreated on a new memory reference every time the component re-renders, while a normal function does. It gets recreated on a new reference if one of the variables inside useCalback
's dependency array changes.
Now, why you would wanna bother with this? Well It's worth it whenever the normal behaviour of a function is problematic for you. For example if you have that function in the dependency array of an useEffect
, or if you pass it down to a component that is memoized with memo
.
The callback of an
useEffect
gets called on the first render and every time one of the variables inside the dependency array changes. And since normally a new version of that function is created on every render, the callback might gets called infinitely. SouseCallback
is used to memoize it.
A memoized component with
memo
re-renders only if itsstate
orprops
changes, not cause its parent re-renders. And since normally a new version of that passed function asprops
is created, when the parent re-renders, the child component gets a new reference, hence it re-renders. SouseCallback
is used to memoize it.
To illustrate I created the below working React application. Click on that button to trigger re-renders of the parent and watch the console. Hope it clears things up!
const MemoizedChildWithMemoizedFunctionInProps = React.memo(
({ memoizedDummyFunction }) => {
console.log("MemoizedChildWithMemoizedFunctionInProps renders");
return <div></div>;
}
);
const MemoizedChildWithNonMemoizedFunctionInProps = React.memo(
({ nonMemoizedDummyFunction }) => {
console.log("MemoizedChildWithNonMemoizedFunctionInProps renders");
return <div></div>;
}
);
const NonMemoizedChild = () => {
console.log("Non memoized child renders");
return <div></div>;
};
const Parent = () => {
const [state, setState] = React.useState(true);
const nonMemoizedFunction = () => {};
const memoizedFunction = React.useCallback(() => {}, []);
React.useEffect(() => {
console.log("useEffect callback with nonMemoizedFunction runs");
}, [nonMemoizedFunction]);
React.useEffect(() => {
console.log("useEffect callback with memoizedFunction runs");
}, [memoizedFunction]);
console.clear();
console.log("Parent renders");
return (
<div>
<button onClick={() => setState((prev) => !prev)}>Toggle state</button>
<MemoizedChildWithMemoizedFunctionInProps
memoizedFunction={memoizedFunction}
/>
<MemoizedChildWithNonMemoizedFunctionInProps
nonMemoizedFunction={nonMemoizedFunction}
/>
<NonMemoizedChild />
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
It's to know that memoizing is not free, doing it wrong is worse. Here is the most complete and shortest ressource I have ever seen to understand React's render process in depth, to know when it worths memoizing and how doing it properly: React Render Tutorial.
In your case, using useCallback
for onUploadPress
is a waste, cause a non memoized
function, pickPhotoFromLibrary
, is in the dependency array. Also it's a waste if TouchableOpacity
is not memoized with memo
, which I'm not sure it's.
As a side note there is useMemo
, which behaves and used like useCallback
to memoize non function but referenced values such as objects
and arrays
for the same reasons, or to memoize any result of a heavy calculation that you don't wanna repeat between renders.
Answered By - yousoumar
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.