Usually, this warning shows up when:
- The parent (currently
App) component updates very often and causesWebDisplaycomponent to re-render. In the provided snippet, every 30 milliseconds; - At least one prop passed to
RenderHTMLis referentially unstable between each re-render. In the provided snippet,tagsStylesreference changes on every re-render.
Notice that between every update of the App component caused by the useEffect hook, the html prop passed to WebDisplay is unchanged. But WebDisplay is re-rendered anyway because it is not “pure”.
For this very reason, a pretty straightforward solution is to wrap WebDisplay in React.memo:
const WebDisplay = React.memo(function WebDisplay({html}) {
const {width: contentWidth} = useWindowDimensions();
const tagsStyles = {
a: {
textDecorationLine: 'none',
},
};
return (
<RenderHtml
contentWidth={contentWidth}
source={{html}}
tagsStyles={tagsStyles}
/>
);
});
You can learn more about this technique in the official documentation.
Note: “pure” terminology comes from React PureComponent class. I think
React.purewould have been less ambiguous thanReact.memobecause we now use “memoization” to designate optimization techniques applying to both components and props, which can be confusing. I prefer to preserve “pure” terminology for components, and “memoization” for props.
Another solution is to move tagsStyles outside the WebDisplay function body. In that case, it will be instantiated only once and become referentially stable. Because RenderHtml itself is pure, it won’t re-render its own subcomponents and the warning should disappear:
const tagsStyles = {
a: {
textDecorationLine: 'none',
},
};
function WebDisplay({html}) {
const {width: contentWidth} = useWindowDimensions();
return (
<RenderHtml
contentWidth={contentWidth}
source={{html}}
tagsStyles={tagsStyles}
/>
);
};
Note: It’s a great habit to move any props which does not depend on any other state or prop outside of the body of a functional component.
Finally, if your use case involves tagsStyles depending on a prop, you could memoize it with React.useMemo:
function WebDisplay({html, anchorColor}) {
const {width: contentWidth} = useWindowDimensions();
const tagsStyles = React.useMemo(
() => ({
a: {
color: anchorColor,
textDecorationLine: 'none',
},
}),
[anchorColor],
);
return (
<RenderHtml
contentWidth={contentWidth}
source={{html}}
tagsStyles={tagsStyles}
/>
);
}
More information on this hook in the official documentation.
If you still don’t have a clear mental model of how component updates work in React, I suggest those readings to enhance your skills:
- reactjs.org, Reconciliation
- reactjs.org, Optimizing Performance, Avoid Reconciliation