我有一组React组件,它们从map函数接收道具,但是问题是这些组件在任何状态更新时都可以安装和卸载。这不是数组键的问题。请参阅codesandbox链接。
const example = () => {
const components = [
(props: any) => (
<LandingFirstStep
eventImage={eventImage}
safeAreaPadding={safeAreaPadding}
isActive={props.isActive}
onClick={progressToNextIndex}
/>
),
(props: any) => (
<CameraOnboarding
safeAreaPadding={safeAreaPadding}
circleSize={circleSize}
isActive={props.isActive}
onNextClick={progressToNextIndex}
/>
),
];
return (
<div>
{components.map((Comp, index) => {
const isActive = index === currentIndex;
return <Comp key={`component-key-${index}`} isActive={isActive} />;
})}
</div>
)
}
如果我在component.map
类似的外部渲染它们,则如下所示,该组件会在任何状态更改时保持不变。
<Comp1 isActive={x === y}
<Comp2 isActive={x === y}
很想知道我在这里困惑时做错了什么。
请看看这个Codesandbox。
我相信在声明返回组件的函数数组时我做错了,如您所见,按下按钮时ComponentOne被重新渲染,而第二个组件则没有。
我认为有两个问题:
为了让React有效地重用它们,您需要向它们添加一个key
属性:
return (
<div>
{components.map((Comp, index) => {
const isActive = index === currentIndex;
return <Comp key={anAppropriateKeyValue} isActive={isActive} />;
})}
</div>
);
除非列表的顺序永不改变index
,key
否则不要仅仅使用for (但是列表是静态的就可以了,因为它似乎在您的问题中)。这可能意味着您需要将数组更改为具有键和组件的对象数组。从上面链接的文档中:
如果项目的顺序可能改变,我们不建议对索引使用索引。这会对性能产生负面影响,并可能导致组件状态出现问题。查阅Robin Pokorny的文章,深入了解使用索引作为键的负面影响。如果您选择不为列表项分配显式键,那么React将默认使用索引作为键。
我怀疑您example
每次都在重新创建阵列。这意味着您每次在数组初始化器中创建的函数都会被重新创建,这意味着对React来说,它们与先前的渲染功能不同。而是使这些功能稳定。有两种方法可以做到这一点,但是例如,您可以直接在回调中使用LandingFirstStep
和CameraOnboarding
组件map
。
const components = [
{
Comp: LandingFirstStep,
props: {
// Any props for this component other than `isActive`...
onClick: progressToNextIndex
}
},
{
Comp: CameraOnboarding,
props: {
// Any props for this component other than `isActive`...
onNextClick: progressToNextIndex
}
},
];
然后在map
:
{components.map(({Comp, props}, index) => {
const isActive = index === currentIndex;
return <Comp key={index} isActive={isActive} {...props} />;
})}
还有其他方法来处理它,例如,通过useMemo
或useCallback
,但是对我来说,这是简单的方法-如果需要而不是使用,它为您提供了放置有意义的密钥的地方index
。
这是处理这两种情况并显示何时安装/卸载组件的示例。如您所见,当索引更改时,它们不再卸载/挂载:
const {useState, useEffect, useCallback} = React;
function LandingFirstStep({isActive, onClick}) {
useEffect(() => {
console.log(`LandingFirstStep mounted`);
return () => {
console.log(`LandingFirstStep unmounted`);
};
}, []);
return <div className={isActive ? "active" : ""} onClick={isActive && onClick}>LoadingFirstStep, isActive = {String(isActive)}</div>;
}
function CameraOnboarding({isActive, onNextClick}) {
useEffect(() => {
console.log(`CameraOnboarding mounted`);
return () => {
console.log(`CameraOnboarding unmounted`);
};
}, []);
return <div className={isActive ? "active" : ""} onClick={isActive && onNextClick}>CameraOnboarding, isActive = {String(isActive)}</div>;
}
const Example = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const progressToNextIndex = useCallback(() => {
setCurrentIndex(i => (i + 1) % components.length);
});
const components = [
{
Comp: LandingFirstStep,
props: {
onClick: progressToNextIndex
}
},
{
Comp: CameraOnboarding,
props: {
onNextClick: progressToNextIndex
}
},
];
return (
<div>
{components.map(({Comp, props}, index) => {
const isActive = index === currentIndex;
return <Comp key={index} isActive={isActive} {...props} />;
})}
</div>
);
};
ReactDOM.render(<Example/>, document.getElementById("root"));
.active {
cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句