千万不要在React组件中使用内联组件

这篇文章也可以在我的博客中查看

啊!

无意间踩到这个大坑,救命阿!
而且这看起来是很正常很优雅的操作啊!为什么啊!

内联组件

定义

内联组件?什么东西?

如果你没听说这个概念,不用惊讶,因为是我刚编的名词

好吧,那你指的是什么?

我指的是:

  1. 在组件内部
  2. 定义了一个返回JSX.Element对象的函数
  3. 使用JSX语法调用它

打个比方,以下这个就是一个内联的组件:

interface IChildCompProps {
	prop1: string,
	prop2: string,
}

export default function OuterComponent() {
	const [localDependency, _] = useState<number>(0);

    function TheInlineComponent(props: IChildCompProps) {
        switch (localDependency) {
            case 0:
                return <ChildComponent1 {...props} />;
            case 1:
                return <ChildComponent2 {...props} />;
            default:
                return <ChildComponent3 {...props} />;
        }
    }

	return (
		<div>
			<TheInlineComponent prop1="1" prop2="2"/>
		</div>
	)
}

这看似是一种非常优雅的方式进行了条件渲染,但实事上这种做法极其危险

问题

不触发差异渲染

因为每次重新渲染OuterComponent,都会重新渲染整个TheInlineComponent
没错,无论TheInlineComponent内部有没有改变,都不会触发差异渲染,而是整个重新渲染。

为什么?

原因很简单。因为这是局部函数。
OuterComponent重新渲染时,React会再次调用OuterComponent()函数,因此TheInlineComponent也会被重新定义。

也就是TheInlineComponent已经不是上一个环境中的TheInlineComponent了!

所以React差异渲染引擎会视TheInlineComponent为新的元素,这也是为什么即使TheInlineComponent没有改变,它还是会被无条件重新渲染。

如果你打开浏览器 DevTools,你可以发现每次OuterComponent重新渲染,整个TheInlineComponent都会被替换

更多潜在风险

整个组件替换,意味着这个React组件的生命周期也再次被刷新,因此它会重新挂载(mount),这对元素内部某些React hook(比如本只应该执行一次的useEffect)的行为会造成根本性的影响。

相信我,非常的恐怖!我因为父子组件内存在一些相互作用的state,造成了组件究极死循环重渲染,一秒渲染1000次,还好及时发现,否则CPU该冒烟了

解决

好吧,如果真是如此,又该如何是好?

条件渲染是非常常见的操作。而且对于比较复杂的选择逻辑而言,也不好全部用表达式写在return中。
因此有必要解决这个问题。

移到外面

如果你的选择逻辑实事上并没有依赖于组件本身,那可以将它移到最外层,成为一个独立的组件。由于不再处于OuterComponent内,因此不会被重复定义。

修改调用方式

但是很多情况下,使用内联已经意味着它和组件环境有一定关联了,比如我们的例子中,它依赖了OuterComponentlocalDependency状态,因此我们没法把它移出去,又该如何是好?

其实很简单,想要解决这个问题,只需要打破“内联组件”的最后一个条件:

export default function OuterComponent() {
	// ...

	return (
		<div>
			{TheInlineComponent({prop1: "1", prop2: "2", })}
		</div>
	)
}

没错,使用JSX函数调用的形式使用它,就不会出问题。

React元素生命周期管理

这是因为,React只会管理JSX元素( )的生命周期,而对于函数调用,是不归React管的

说人话就是:
使用JSX实例化JSX.Element,会被React管理生命周期:

<TheInlineComponent prop1="1" prop2="2"/>

使用普通函数调用生成JSX.Element,React不会管:

{TheInlineComponent({prop1: "1", prop2: "2", })}

笼统解释一下:

  • 在JSX实例化中,React看到的JSX元素是,这就是导致上述问题的原因
  • 而在函数调用中,React看到的只是函数返回的,由于这个东西本身是非内联组件,因此不会存在任何重复定义的问题。
  • 区别就在于TheInlineComponent有没有被视为一个JSX元素

总结

所以,注意咯,千万不要因为好康就把本地函数写成内联的组件调用了,很恐怖的哟

你可能感兴趣的:(全栈,react.js,javascript,前端,next.js)