在react.js中加载DOM时的回调


76

当它的DOM元素(包括所有子节点)实际加载到页面上并准备就绪时,我想在我的react.js组件上调用一个回调。具体来说,我有两个想要渲染相同大小的组件,选择具有自然尺寸的任何一个组件的最大值。

看起来componentDidMount并不是我真正想要的,因为每个组件仅被调用一次,但是我希望在组件完成渲染后再次调用我的回调。我以为可以将onLoad事件添加到顶级DOM元素中,但是我想这仅适用于某些元素,例如<body><img>

Answers:


69

在componentDidMount中添加onload侦听器

class Comp1 extends React.Component {
 constructor(props) {
    super(props);
    this.handleLoad = this.handleLoad.bind(this);
 }

 componentDidMount() {
    window.addEventListener('load', this.handleLoad);
 }

 componentWillUnmount() { 
   window.removeEventListener('load', this.handleLoad)  
 }

 handleLoad() {
  $("myclass") //  $ is available here
 }
}

10
还记得添加删除componentWillUnmount() { window.removeEventListener('load', this.handleLoad) }
事件监听器

49

看起来像的组合componentDidMount,并componentDidUpdate会完成这项工作。当DOM可用时,第一个调用在初始渲染之后调用,一旦更新的DOM可用,则在任何后续渲染之后调用第二个调用。就我而言,我都让他们委托一个共同的职能来做同样的事情。


2
但是componentDidUpdate将被多次调用,这是不可行的
John

@John用什么方式不可行?
brianmearns

2
@brianmearns在使用像redux这样的状态管理库时,父项可能会更改很多次,并且每次都会调用componentDidUpdate。如果我们只需要等待初始DOM渲染,则在这种情况下将不可行。在这种情况下,将加载侦听器附加到componentDidMount似乎更好,如以下答案之一所示。
Adarsh Konchady

这仅适用于顶层组件吗?这种方法如何确保所有子组件均已完成渲染?
mpelzsherman

当我使用这种方法时,它给了我最大的深度,因为它创建了一个无限循环
Walls

9

的组合componentDidMount,并componentDidUpdate会得到与类组件的代码完成任务。但是,如果您要使用全部功能组件编写代码,the Effect Hook将会做得很好,它与componentDidMount和相同componentDidUpdate

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

https://reactjs.org/docs/hooks-effect.html


1
我喜欢这个答案的方向,但是您的示例无法解决问题。
瑞安

我认为问题与负载事件有关。使用useEffect钩子,您可以检测到用户访问“视图/页面/ ....”的时刻,而不必等待屏幕上所有元素的出现
saomi


4

我发现在这简单的包装代码componentDidMountcomponentDidUpdatesetTimeout用0毫秒的时间确保在执行以下操作之前,已使用React更改来更新浏览器DOM。setTimeout的功能。

像这样:

componentDidMount() {
    setTimeout(() => {
        $("myclass") //  $ is available here
    }, 0)
}

这会将匿名函数放在JS事件队列中,以在当前运行的React堆栈框架完成后立即运行。


setTimeout有什么意义?我了解事件队列的作用,但是为什么您认为这是必需的呢?根据react docs,在加载/更新DOM元素之前,不会调用这两种方法。通过添加setTimeout,您将引入竞争条件以及后续更新。
brianmearns

对于延迟加载图像,我也使用了相同的技术。如果我直接在componentDidMount中调用lazyLoadImages函数,则位于浏览器选项卡中的页面加载指示器将一直旋转,直到加载所有图像为止。但是,如果使用100ms参数在setTimeout中包装lazyLoadImages函数,则这种情况将有所不同。当图像开始在后台加载时,加载指示器将很快停止旋转。setTimeout使体验更好。将延迟加载功能绑定到窗口onload事件上的功能相同,但是不适用于SPA。
Nero

3

在现代浏览器中,它应该像

try() {
     if (!$("#element").size()) {
       window.requestAnimationFrame(try);
     } else {
       // do your stuff
     }
};

componentDidMount(){
     this.try();
}

6
嘿,除了现代浏览器不使用jquery = P。不过,这是有关使用动画框架的一个很好的提示,假设它应该实时更新。
brianmearns '18

1

下面是我想在DOM准备好后尝试使用document.getElementsByClassName获取类之前想到的。我从componentDidMount()生命周期方法中调用了此函数。

     changeIcon() {
            if (
                document.getElementsByClassName('YOURCLASSNAME')
                    .length > 0 &&
                document.getElementsByClassName('YOURCLASSNAME').length > 0
            ) {
                document.getElementsByClassName(
                    'YOURCLASSNAME'
                )[0].className = 'YOUR-NEW-CLASSNAME';
                document.getElementsByClassName(
                    'YOUR-OTHER-EXISTING-CLASSNAME'
                )[0].style.display = 'block';
            } else {
                setTimeout(this.changeIcon, 500);
            }
     }

0

您可以使用useRef挂钩查看容器元素。请注意,您需要观看refcurrent值,否则它将不起作用。

例:

  const containerRef = useRef();
  const { current } = containerRef;

  useEffect(setLinksData, [current]);

return (
    <div ref={containerRef}>
      // your child elements...
    </div>
)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.