了解返回映射子组件的React功能组件

时间:2019-12-02 20:27:08

标签: javascript reactjs

今天,我花了很多时间调试问题,每次重新渲染时都会在子组件中重置状态。在最终解决它之后,我意识到我不完全了解React功能组件和Array.map()是如何协同工作的,这就是为什么我希望有人可以阐明我遇到的问题:

比方说,我有一个ItemWrapper组件,它返回了ItemListA组件。 ItemListA映射到数组,并返回Item组件的列表。每个Item组件都有其自己的状态,这些状态会随着某些操作而改变。

我刚开始的方式:

const ItemWrapper = ({ items }) =>  {
    const [someState, setSomeState] = useState(null);
    const someFunction = value => setSomeState(value);

    ...

    const ItemListA = () => items.map(item =>
        <Item
            key={item.id}
            item={item}
            callback={someFunction}
        />
    )

    ...

    return (
        <div>
            <ItemListA />
        </div>
    );
};

问题:每当someFunction子级之一调用Item回调时,这会导致ItemWrapper重新呈现并重置所有其他{{1 }}孩子。

通过将项目列表存储在局部变量而不是组件中来解决该问题:

Item

我不觉得我完全了解这里发生的事情。我的猜测是,以某种方式将项目列表存储在功能组件中,这样const ItemWrapper = ({ items }) => { const [someState, setSomeState] = useState(null); const someFunction = value => setSomeState(value); ... const itemListB = items.map(item => <Item key={item.id} item={item} callback={someFunction} /> ); ... return ( <div> {itemListB} </div> ); }; 组件及其子代(触发回调的子代除外-不知道为什么)被破坏,然后在任何时候重建ItemListA组件被重新渲染,这意味着它们先前状态没有任何痕迹。是因为ItemWrapper是一个函数,并且每次重新渲染都会创建一个新引用吗?

1 个答案:

答案 0 :(得分:3)

  

是因为ItemListA是一个函数,并且每次重新渲染都会创建一个新引用吗?

是的,差不多。

在确定对DOM进行什么更改时,react将之前的虚拟dom与之后的虚拟dom进行比较,并查找更改。进行详尽的比较会很昂贵,因此他们做出了一些假设来加快速度。这些假设之一是,如果组件的类型已更改,则假定整个子树都已更改。 (有关更多信息,请参见react的article on reconciliation

因此,在这种情况下,react第一次看到ItemListA,第二次看到ItemListA,它们是不同的组件类型。它们看起来与我们的眼睛非常相似,但是它们是不同的参考,这意味着它们的反应不同。因此,必须先卸载旧的并重新安装新的。

使用第二个代码,您不会在每个渲染上都创建一种新类型的组件,而只是创建一个其中包含元素的数组。这两个数组是不同的引用,但这没关系,因为它不是一种组件。