I have a button component that has a button inside that has a state passed to it isActive and a click function. When the button is clicked, the isActive flag will change and depending on that, the app will fetch some data. The button's parent component does not rerender. I have searched on how to force stop rerendering for a component and found that React.memo(YourComponent) must do the job but still does not work in my case. It also make sense to pass a check function for the memo function whether to rerender or not which I would set to false all the time but I cannot pass another argument to the function. Help.
button.tsx
interface Props {
isActive: boolean;
onClick: () => void;
}
const StatsButton: React.FC<Props> = ({ isActive, onClick }) => {
useEffect(() => {
console.log('RERENDER');
}, []);
return (
<S.Button onClick={onClick} isActive={isActive}>
{isActive ? 'Daily stats' : 'All time stats'}
</S.Button>
);
};
export default React.memo(StatsButton);
parent.tsx
const DashboardPage: React.FC = () => {
const {
fetchDailyData,
fetchAllTimeData,
} = useDashboard();
useEffect(() => {
fetchCountry();
fetchAllTimeData();
// eslint-disable-next-line
}, []);
const handleClick = useEventCallback(() => {
if (!statsButtonActive) {
fetchDailyData();
} else {
fetchAllTimeData();
}
setStatsButtonActive(!statsButtonActive);
});
return (
<S.Container>
<S.Header>
<StatsButton
onClick={handleClick}
isActive={statsButtonActive}
/>
</S.Header>
</S.Container>
)
}
fetch functions are using useCallback
export const useDashboard = (): Readonly<DashboardOperators> => {
const dispatch: any = useDispatch();
const fetchAllTimeData = useCallback(() => {
return dispatch(fetchAllTimeDataAction());
}, [dispatch]);
const fetchDailyData = useCallback(() => {
return dispatch(fetchDailyDataAction());
}, [dispatch]);
return {
fetchAllTimeData,
fetchDailyData,
} as const;
};
You haven't posted all of
parent.tsx, but I assume thathandleClickis created within the body of the parent component. Because the identity of the function will be different on each rendering of the parent, that causesuseMemoto see the props as having changed, so it will be re-rendered.Depending on if what's referenced in that function is static, you may be able to use
useCallbackto pass the same function reference to the component on each render.Note that there is an RFC for something even better than
useCallback; ifuseCallbackdoesn't work for you look at howuseEventis defined for an idea of how to make a better static function reference. It looks like that was even published as a newuse-event-callbackpackage.Update:
It sounds like
useCallbackwon't work for you, presumably because the referenced variables used by the callback change on each render, causinguseCallbackto return different values, thus making the prop different and busting the cache used byuseMemo. Try thatuseEventCallbackapproach. Just to illustrate how it all works, here's a naive implementation.This
useEventCallbackalways returns the same memoized function, so you'll pass the same value to your props and not cause a re-render. However, when the function is called it calls the version of the function passed intouseEventCallbackinstead. You'd use it like this in your parent component: