React.memo失效问题排查:如何正确缓存子组件的子组件?
问题分析与解决方案
你遇到的核心问题确实和Icon属性的传递方式有关——每次ToolbarComponent重新渲染时,你都在创建一个全新的<MyOtherIcon /> JSX元素实例,这就导致即使React.memo里的比较函数检查icon是否相等,也会因为前后两个icon是不同的对象引用而返回false,最终触发ToolbarButton的重渲染。
让我们拆解一下原因:
- 当
CIsTrue状态变化时,App会重新渲染,进而导致ToolbarComponent重新渲染 - 在
ToolbarComponent的渲染过程中,<MyOtherIcon />会被重新创建为一个新的React元素对象(哪怕它的逻辑完全没变) - 这个新的
icon属性传递给ToolbarButton时,和之前的icon引用不相等,你的比较函数就会认为props变化了,于是触发重渲染
解决方案1:传递组件类型而非JSX元素
与其直接传递渲染好的JSX元素,不如把Icon组件本身作为props传递,然后在ToolbarButton内部渲染它。这样传递的是同一个组件引用,不会每次都创建新对象:
修改ToolbarComponent:
export const ToolbarComponent: React.FunctionComponent<ToolbarComponentProps> = (props) => { const { AIsTrue, BIsTrue, CIsTrue } = props; return ( <Stack> <ToolbarButton text="FirstButton" icon={MyIcon} visible={AIsTrue || BIsTrue || CIsTrue } /> <ToolbarButton text="SecondButton" icon={MyOtherIcon} visible={BIsTrue} /> </Stack> ); };
修改ToolbarButton的props定义和渲染逻辑:
export interface IToolbarButtonProps { text?: string; icon: React.ElementType; // 改为组件类型 visible?: boolean; } export const ToolbarButton: React.FunctionComponent<IToolbarButtonProps> = React.memo( (props) => { const { text, icon: Icon, visible } = props; const buttonClass = visible ? 'blah' : ''; console.log('I rendered - ' + text); return ( <Button className={buttonClass}> <Stack horizontal> <Icon /> {/* 在这里渲染组件 */} <Text>{text}</Text> </Stack> </Button> ); }, (prevProps, nextProps) => { // 现在icon是组件类型,引用不会变,加上其他props的检查 return prevProps.text === nextProps.text && prevProps.visible === nextProps.visible && prevProps.icon === nextProps.icon; }, );
解决方案2:用useMemo缓存JSX元素
如果你更倾向于保持当前传递JSX元素的方式,可以在ToolbarComponent里用useMemo把Icon元素缓存起来,确保每次渲染时引用不变:
export const ToolbarComponent: React.FunctionComponent<ToolbarComponentProps> = (props) => { const { AIsTrue, BIsTrue, CIsTrue } = props; // 缓存Icon元素,只有当依赖变化时才重新创建 const firstIcon = useMemo(() => <MyIcon />, []); const secondIcon = useMemo(() => <MyOtherIcon />, []); return ( <Stack> <ToolbarButton text="FirstButton" icon={firstIcon} visible={AIsTrue || BIsTrue || CIsTrue } /> <ToolbarButton text="SecondButton" icon={secondIcon} visible={BIsTrue} /> </Stack> ); };
这样即使ToolbarComponent重新渲染,secondIcon的引用也不会变,你的React.memo比较函数就能正确识别props未变化,避免不必要的重渲染。
额外提示
你的React.memo比较函数目前只检查了icon,建议也加上text和visible的检查,确保所有相关props都被正确比较,避免其他不必要的重渲染。
内容的提问来源于stack exchange,提问作者Anavi Lohia
相关产品推荐
相关产品推荐

