How the memorized callback function works? In some articles I read that the function is recreated if we do not use useCallback. But if it is recreated, should it be different from the prev version? In my code I didn't notice that there was a difference in callback functions. My question is: Why in both cases, my set size is 1?
from off doc useCallback
Returns a memoized callback.
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
import { useCallback } from "react";
const dataSource = [
{
id: 1,
model: "Honda",
color: "red",
},
{
id: 2,
model: "Mazda",
color: "yellow",
},
{
id: 3,
model: "Toyota",
color: "green",
},
];
const Car = ({ model, color, set, onCarClick }) => {
const onClick = () => onCarClick(model, color);
set.add(onCarClick);
console.log(set.size);
return (
<div onClick={onClick}>
Model: {model} Color: {color}
</div>
);
};
const CarsCallback = ({ cars, set }) => {
const onCarClick = (model, color) => {
console.log(model, color);
};
console.log("CarsCallback");
return (
<>
{cars.map((car) => {
return (
<Car
key={car.id}
set={set}
{...car}
onCarClick={onCarClick}
/>
);
})}
</>
);
};
const CarsUseCallback = ({ cars, set }) => {
const onCarClick = useCallback((model, color) => {
console.log(model, color);
}, []);
console.log("CarsUseCallback");
return (
<>
{cars.map((car) => {
return (
<Car
key={car.id}
{...car}
set={set}
onCarClick={onCarClick}
/>
);
})}
</>
);
};
export default function App() {
return (
<div className="App">
<CarsCallback cars={dataSource} set={new Set()} />
<CarsUseCallback cars={dataSource} set={new Set()} />
</div>
);
}
CodePudding user response:
Because CarsUseCallback and CarsCallback was triggered once.
We can see the only one log of CarsUseCallback and CarsCallback.
If we re-render the CarsUseCallback and CarsCallback, we can see the size is 1 and 2.
const CarsCallback = ({ cars, set }) => {
const [count, setCount] = useState(1);
console.log('CarsCallback');
useEffect(() => {
setCount(2);
}, []);
// ...
};
const CarsUseCallback = ({ cars, set }) => {
const [count, setCount] = useState(1);
console.log('CarsUseCallback');
useEffect(() => {
setCount(2);
}, []);
// ...
}
CodePudding user response:
In some articles I read that the function is recreated if we do not use useCallback
Function is re-created irrespective of whether you use the useCallback hook or not. Difference is that with useCallback, re-created function is discarded and the memoized function is returned if the dependencies of the useCallback hook haven't changed.
But if it is recreated, should it be different from the prev version?
Yes, each new function reference is a difference function, i.e. different object.
In my code I didn't notice that there was a difference in callback functions. My question is: Why in both cases, my set size is 1?
Because you need to re-render your component to re-create the function. In your code, callback function only gets created once in both components, hence the size of Set is always 1.
Also, you need to get rid of the following line of code inside the Car component:
const onClick = () => onCarClick(model, color);
What you want to add in the Set is the onCarClick function that is passed as a prop from the parent component. With onClick, you are always creating a new function irrespective of whether you use the useCallback hook or not.
Following demo shows the effect of useCallback hook. Click the re-render button to render the components, causing the callback function to be re-created.
const dataSource = [
{ id: 1, model: "Honda", color: "red" }
];
const Car = ({ model, color, set, onCarClick }) => {
//const onClick = () => onCarClick(model, color); // remove this
set.add(onCarClick);
console.log(set.size);
return (
<div onClick={onCarClick}>
Model: {model} Color: {color}
</div>
);
};
const CarsCallback = ({ cars, set }) => {
const onCarClick = (model, color) => {
console.log(model, color);
};
console.log("CarsCallback");
return (
<div>
{cars.map((car) => {
return <Car key={car.id} set={set} {...car} onCarClick={onCarClick} />;
})}
</div>
);
};
const CarsUseCallback = ({ cars, set }) => {
const onCarClick = React.useCallback((model, color) => {
console.log(model, color);
}, []);
console.log("CarsUseCallback");
return (
<div>
{cars.map((car) => {
return <Car key={car.id} {...car} set={set} onCarClick={onCarClick} />;
})}
</div>
);
};
const set1 = new Set();
const set2 = new Set();
function App() {
const [state, setState] = React.useState();
return (
<div className="App">
<CarsCallback cars={dataSource} set={set1} />
<CarsUseCallback cars={dataSource} set={set2} />
<button onClick={() => setState({})}>Re-render</button>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
