【问题标题】:What is determining the order in which these components' 'mount' functions execute?是什么决定了这些组件的“挂载”功能的执行顺序?
【发布时间】:2020-10-30 14:40:47
【问题描述】:

在我的代码中,app 是最高(功能)组件。它渲染piggybank(这是一个类组件),它渲染piggychild(这是一个功能组件)。

每个组件 'console.logs' 本身的名称(在类组件的render 方法中;只是在功能组件的函数中)并具有“安装”功能(对于类组件,此表示componentDidMount 方法;对于功能组件,这表示useEffect 钩子,其回调的第二个参数是一个空数组)将'mount' 记录到控制台。


如图所示,PiggyBank 的挂载首先触发,然后是 PiggyChild 的,然后是 App 的。

React 中是否有规则来管理组件的“挂载”功能发生的顺序?

或者仅仅知道在所有组件渲染之后发生挂载函数就足够了?

【问题讨论】:

  • 是的,我也很好奇。但也许 useEffect 返回函数在顺序上与 componentDidMount 函数不完全相同。所以也许只使用一种类型的组件测试会更准确。

标签: javascript reactjs


【解决方案1】:

useEffect 的时间与 componentDidMount 不同。最接近CDM的钩子其实是useLayoutEffect。

这里有一点来自ReactTraining's blog on useEffect

它们在不同的时间运行

首先,让我们谈谈每个时间。 componentDidMount 运行 组件安装后。正如文档所说,如果您设置状态 立即(同步)然后 React 知道如何触发额外的 渲染并使用第二个渲染的响应作为初始 UI,因此 用户没有看到闪烁。想象一下,您需要读取 a 的宽度 带有 componentDidMount 的 DOM 元素并希望更新状态以反映 关于宽度的东西。想象一下这一系列事件:

  1. 组件首次呈现。
  2. render() 的返回值用于挂载新的 DOM。
  3. componentDidMount 立即触发并设置状态(不在异步 回调)
  4. 状态改变意味着再次调用render()并返回新的JSX 它取代了之前的渲染。
  5. 浏览器只显示第二个渲染以避免闪烁。

很高兴这就是我们需要它时的工作方式。但大多数 时间我们不需要这种预先优化的方法,因为我们正在做 异步网络调用,然后在绘制后设置状态 屏幕。

componentDidMount 和 useEffect 在挂载后运行。然而 useEffect 在油漆被提交到屏幕后运行,而不是 前。这意味着如果您需要从 DOM,然后同步设置状态以创建新的 UI。

当我们需要旧的行为时如何恢复它?

useLayoutEffect 被设计为与 组件安装。所以 useLayoutEffect(fn, []) 是更接近的匹配 到 componentDidMount() 比 useEffect(fn, []) - 至少从 时间点。

这是否意味着我们应该使用 useLayoutEffect 代替?

可能不会。

如果您确实想通过同步设置状态来避免闪烁, 然后使用 useLayoutEffect。但由于这些是罕见的情况,你会想要 大部分时间都使用 useEffect。

我在您描述的场景中添加了一个具有布局效果和普通效果的示例:

运行结果如下。需要注意的主要一点是,布局效果通过 componentDidMount 生命周期钩子以合理的顺序发生,而效果在以后发生。

应用程序

存钱罐

小猪佩奇

PiggyChild 布局效果

存钱罐坐骑

应用布局效果

小猪效应

应用效果

const {useEffect, useLayoutEffect} = React;
function App(){
 console.log('App');
 useLayoutEffect(()=>console.log('App LayoutEffect'),[]);
 useEffect(()=>console.log('App Effect'),[]);

 return <PiggyBank/>;
}
class PiggyBank extends React.Component{
 componentDidMount(){
  console.log('PiggyBank Mount');
 }
 render(){
  console.log('PiggyBank');
  return <PiggyChild/>
 }
}
function PiggyChild(){
 console.log('PiggyChild');
 useLayoutEffect(()=>console.log('PiggyChild LayoutEffect'),[]);
 useEffect(()=>console.log('PiggyChild Effect'),[]);
 return <div/>;
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>

如果没有 LayoutEffects,顺序将是:

  1. 渲染函数中的console.logs,按照从父到子的顺序,因为渲染每个组件的一部分是渲染子

  2. 然后 componentDidMount 在安装完成后立即运行。如果有多个具有 componentDidMounts 或 useLayoutEffects 的组件,则这些组件将在它们完成安装时按从子到父的顺序调用,但在提交绘制之前(如上所述)。

  3. useEffects 按照从子级到父级的顺序运行,因为它们按照该顺序完成安装,但由于上述时间安排,它们将始终在 componentDidMounts 和 useLayoutEffects 之后运行。

const {useEffect} = React;
function App(){
 console.log('App');
 // useLayoutEffect(()=>console.log('App LayoutEffect'),[]);
 useEffect(()=>console.log('App Effect'),[]);

 return <PiggyBank/>;
}
class PiggyBank extends React.Component{
 componentDidMount(){
  console.log('PiggyBank Mount');
 }
 render(){
  console.log('PiggyBank');
  return <PiggyChild/>
 }
}
function PiggyChild(){
 console.log('PiggyChild');
 // useLayoutEffect(()=>console.log('PiggyChild LayoutEffect'),[]);
 useEffect(()=>console.log('PiggyChild Effect'),[]);
 return <div/>;
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>

【讨论】:

  • 添加useLayoutEffect 让事情变得过于复杂。我希望您的示例仅包含componentDidMountuseEffect 用于AppPiggyBankPiggyChild。然后解释了原因
  • 不过就是这样,useLayoutEffect 与 componentDidMount 在同一时间运行。无论如何,我添加了你要求的内容。
【解决方案2】:

其实它是绝对有意义的,层次结构中的useEffect顺序与componentDidMount中的顺序相同。 问题是 componentDidMount 在所有子事件挂载时触发(因此您会看到第一个子事件,然后是父事件) - 与您看到的顺序相同。

有关 componentDidMount 顺序的更多详细信息,您可以在此处查看 - Order of componentDidMount in React components hierarchy

【讨论】:

  • 你可能误读了我得到的结果。当谈到“安装”时,我居中;底部;最佳。您链接的文章仅讨论类组件,而我的示例混合了类和功能组件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
相关资源
最近更新 更多