Reputation: 6075
I have an Icon component that draw an icon and which is blinking because the parent is making it rerender for nothing. I don't understand why this is happening and how to prevent this.
Here is a snack that shows the issue.
We emulate the parent changes with a setInterval.
We emulate the icon rerendering by logging 'rerender' in the console.
Here is the code:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
// or any pure javascript modules available in npm
let interval = null
const Child = ({name}) => {
//Why would this child still rerender, and how to prevent it?
console.log('rerender')
return <Text>{name}</Text>
}
const ChildContainer = ({name}) => {
const Memo = React.memo(Child, () => true)
return <Memo name={name}/>
}
export default function App() {
const [state, setState] = React.useState(0)
const name = 'constant'
// Change the state every second
React.useEffect(() => {
interval = setInterval(() => setState(s => s+1), 1000)
return () => clearInterval(interval)
}, [])
return (
<View>
<ChildContainer name={name} />
</View>
);
}
If you could explain me why this is happening and what is the proper way to fix it, that would be awesome!
Upvotes: 2
Views: 2483
Reputation: 2930
If you move const Memo = React.memo(Child, () => true)
outside the ChildContainer
your code will work as expected.
While ChildContainer
is not a memoized component, it will be re-rendered and create a memoized Child
component on every parent re-render.
By moving the memoization outside of the ChildContainer
, you safely memoize your component Child
once, and no matter how many times ChildContainer
will be called, Child
will only run one time.
Here is a working demo. I also added a log on the App
to track every re-render, and one log to the ChildComponent
so you can see that this function is called on every re-render without actually touching Child
anymore.
You could also wrap Child
with React.memo
directly:
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
// or any pure javascript modules available in npm
let interval = null;
const Child = React.memo(({ name }) => {
//Why would this child still rerender, and how to prevent it?
console.log("memoized component rerender");
return <Text>{name}</Text>;
}, () => true);
const ChildContainer = ({ name }) => {
console.log("ChildContainer component rerender");
return <Child name={name} />;
};
export default function App() {
const [state, setState] = React.useState(0);
const name = "constant";
// Change the state every second
React.useEffect(() => {
interval = setInterval(() => setState(s => s + 1), 1000);
return () => clearInterval(interval);
}, []);
console.log("App rerender");
return (
<View>
<ChildContainer name={name} />
</View>
);
}
Upvotes: 6