if useCallback used to prevent what declare function every render,
how about declare function outside of component(case1) instead declare inside(case3)?
function case1() {
/* do stuff */ // declared at JS script loaded(not sure)
}
const App = () => {
const case2 = () => {/* do stuff */}; // declared every render
const case3 = useCallback(() => {/* do stuff */}, []); // declared at first render
return (<></>);
}
You can only pass some variables as parameters for the case1 function outside of the component. See the below example:
import React, { useCallback } from 'react';
const Button = React.memo<{ onClick: any; children: any }>(({ onClick, children }) => {
console.log('render child');
return <button onClick={onClick}>{children}</button>;
});
const case1 = (dep) => {
console.log('expensive work in case1, dep: ', dep);
};
export default function App({ dep }) {
const case3 = useCallback(() => {
console.log('expensive work in case3, dep: ', dep);
}, [dep]);
const case1Bound = case1.bind(this, dep);
return (
<div>
{/* use function declared outside of a component */}
{/* <Button onClick={() => case1(dep)}>click me</Button> */}
<Button onClick={case1Bound}>click me</Button>
{/* useCallback */}
{/* <Button onClick={case3}>click me</Button> */}
</div>
);
}
Suppose the case1 function depends on the dep props and is triggered by the click event of the Button component. You can only pass the dep as a parameter through the arrow function or the .bind() method. As a result, it creates a new function for the onClick prop every time the App component renders. It will cause the React.memo() shallow comparison return false, the Button component will re-render. See below test:
import 'jest';
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import App from './index';
describe('useCallback-vs-function-outside-component', () => {
test('should pass', () => {
const { rerender } = render(<App dep={1} />);
const button = screen.getByText(/click me/);
fireEvent.click(button);
rerender(<App dep={1} />);
fireEvent.click(button);
});
});
Logs:
render child
expensive work in case1, dep: 1
render child
expensive work in case1, dep: 1
With useCallback:
useCallbackwill return a memoized version of the callback that only changes if one of the dependencies has changed.
So the dep is NOT changed, the case3 function is the function created for the first time, and useCallback will return directly from memory instead of creating a new one. The Button onClick prop is NOT changed, the shallow comparison of React.memo() will return true, so it will render only once.
Logs:
render child
expensive work in case3, dep: 1
expensive work in case3, dep: 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With