在下面的码砂箱中,我们有一个<Child />组件,它可以在<div />中呈现,也可以不呈现,具体取决于状态。
import React, { useState, memo } from "react";
const Child = memo(
({ n }) => {
console.log("Re-rendered child");
return <span>Child {n}</span>;
},
() => true
);
export default function App() {
const [shouldWrapChildComponent, setShouldWrapChildComponent] = useState(
false
);
return (
<div>
<button
onClick={() =>
setShouldWrapChildComponent(
(shouldWrapChildComponent) => !shouldWrapChildComponent
)
}
>
Toggle Wrapper
</button>
<br />
{[0, 1, 2, 3, 4, 5].map((n) => {
return shouldWrapChildComponent ? (
<div>
<Child n={n} />
</div>
) : (
<Child n={n} />
);
})}
</div>
);
}如您所见,<Child />组件正在使用React.memo来防止重新呈现。
但是,当组件树更改时,不会阻止重新呈现(这是有意义的)。如果shouldWrapChildComponent是true,那么<Child />将在<div />内部呈现,否则<Child />将呈现<div />过去呈现的位置。这就是我所说的“组件树改变”的意思。
是否有一种方法可以重写组件,使组件树不会改变,从而可以利用React.memo?或者,考虑到我们总是需要有条件地包装<Child />组件,那么没有办法保留<Child />的呈现输出
发布于 2021-10-23 10:12:45
但是,当组件树更改时,不会阻止重新呈现(这是有意义的)。如果shouldWrapChildComponent为真,则将呈现在a的内部,否则将呈现用于呈现的位置。这就是我所说的“组件树改变”的意思。
让我们谈谈上面的引号,让我们做一个小小的更改,并删除div元素:
{[0, 1, 2, 3, 4, 5].map((n) => {
return shouldWrapChildComponent ? (
<Child n={n} />
) : (
<Child n={n} />
);
})
}结果是:即使在切换按钮之后,Child组件也只会重新呈现6次。您可以将console.log()放在App组件上并检查控制台,切换按钮将导致整个App重新呈现,但Child组件将不再重新呈现。
因此,render正在正确地工作,并防止子组件额外重呈现。
但是,当您用另一个元素(这里使用Child )包装您的div组件时,问题出现了,为什么?
为了检查这种行为,我通过创建一个resultArray变量来在呈现之前记录resultArray,从而将主体部分与返回方法分开。
import React, { useState, memo } from "react";
const Child = memo(
({ n }) => {
console.log("Re-rendered child");
return <span>Child {n}</span>;
},
() => true
);
export default function App() {
const [shouldWrapChildComponent, setShouldWrapChildComponent] = useState(
false
);
const resultArray = [0, 1, 2, 3, 4, 5].map((n) => {
return shouldWrapChildComponent ? (
<div>
<Child n={n} />
</div>
) : (
<Child n={n} />
);
})
console.log(`When shouldWrapChildComponent is ${shouldWrapChildComponent} the result array is: `, resultArray);
return (
<div>
<button
onClick={() =>
setShouldWrapChildComponent(
(shouldWrapChildComponent) => !shouldWrapChildComponent
)
}
>
Toggle Wrapper
</button>
<br />
{resultArray}
</div>
);
}当resultArray shouldWrapChildComponent是false时
>(6) [Object, Object, Object, Object, Object, Object]
>0: Object
type: "div" // pay attention here
key: null
ref: null
>props: Object
n: 0 // ----> passing n via props to Child
_owner: FiberNode
_store: Object
>1: Object
>2: Object
>3: Object
>4: Object
>5: Object当resultArray shouldWrapChildComponent是真时
>(6) [Object, Object, Object, Object, Object, Object]
>0: Object
type: null // pay attention here
key: null
ref: null
>props: Object // -----> passing a object instead of Child
>children: Object
>type: Object
>type: ƒ _c() {}
>compare: f () {}
_owner: FiberNode
_store: Object
>1: Object
>2: Object
>3: Object
>4: Object
>5: Object正如您所看到的,结果是完全不同的,所以react将触发每次重新呈现DOM中的新元素。
反应协调算法
当区分两棵树时,首先对两个根元素进行比较。根据根元素的类型,行为是不同的。
因此,当元素type被更改时,Reconciliation算法标记它并尝试重新呈现它,这个重呈现就像组件挂载之前在组件上的第一个呈现(并导致额外的子元素重呈现)。
更多关于反应官方文档。
作为最简单的解决方案
有许多解决方案可以避免这种额外的重呈现,但作为一个简单的解决方案,您可以更改主要部分如下:
{[0, 1, 2, 3, 4, 5].map((n) => {
return shouldWrapChildComponent ? (
<div>
<Child n={n} />
</div>
) : (
<div style={{display: "inline"}}>
<Child n={n} />
</div>
);
})
}https://stackoverflow.com/questions/69684810
复制相似问题