【问题标题】:Callback when DOM is loaded in react.jsreact.js中加载DOM时的回调
【发布时间】:2014-11-21 11:26:34
【问题描述】:

我希望在我的 react.js 组件的 DOM 元素(包括所有子节点)实际加载到页面上并准备就绪时调用回调。具体来说,我有两个组件要呈现相同的大小,选择具有较大自然大小的组件中的最大值。

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

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    在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
     }
    }
    

    【讨论】:

    • 还记得添加remove eventlistener componentWillUnmount() { window.removeEventListener('load', this.handleLoad) }
    【解决方案2】:

    看起来componentDidMountcomponentDidUpdate 的组合可以完成工作。第一个在初始渲染后调用,当 DOM 可用时,第二个在任何后续渲染后调用,一旦更新的 DOM 可用。就我而言,我都让他们委托一个共同的功能来做同样的事情。

    【讨论】:

    • 但是componentDidUpdate会被多次调用,这是不可行的
    • @John 在什么方面不可行?
    • @brianmearns 在使用像 redux 这样的状态管理库时,父 props 可以更改很多次,并且每次都会调用 componentDidUpdate。如果我们只需要等待初始 DOM 渲染,在这种情况下将不可行。在这种情况下,在 componentDidMount 中附加负载侦听器似乎更好,如下面的答案之一所示。
    • 这仅适用于顶级组件吗?这种方法如何确保所有子组件都完成渲染?
    • 当我使用这个方法时,它给了我一个最大的深度,因为它创建了一个无限循环
    【解决方案3】:

    componentDidMountcomponentDidUpdate 的组合将在带有类组件的代码中完成工作。 但是,如果您要编写包含全部功能组件的代码,the Effect Hook 会做得很好,它与 componentDidMountcomponentDidUpdate 相同。

    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

    【讨论】:

    • 我喜欢这个答案的方向,但是你的例子并没有解决问题。
    • 我认为这个问题与加载事件有关。使用 useEffect 挂钩,您可以检测到用户访问“视图/页面/....”的那一刻,您无需等待屏幕中出现所有元素
    【解决方案4】:

    我将 componentDidUpdate 应用于表格以使所有列的高度相同。它的工作原理与 jquery 中的 $(window).load() 相同。

    例如:

    componentDidUpdate: function() {
            $(".tbl-tr").height($(".tbl-tr ").height());
        }
    

    【讨论】:

    • 不幸的是,componentDidUpdate 不会被调用来进行初始渲染,因此在道具或状态更改之前不会应用您的函数:reactjs.org/docs/react-component.html#componentdidupdate
    • 为什么在 react 中使用 jquery?作者要求 React 解决方案,因此无需引入新的额外依赖项,实际上根本没有用
    【解决方案5】:

    我发现,只需在 componentDidMountcomponentDidUpdate 中使用 setTimeout 包装代码,时间为 0 毫秒,可确保浏览器 DOM 在执行 setTimeout 之前已使用 React 更改进行更新功能。

    像这样:

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

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

    【讨论】:

    • setTimeout 的意义何在?我了解它在事件队列方面的作用,但是您为什么认为这里有必要?根据反应文档,在加载/更新 DOM 元素之前,不会调用这些方法。通过添加 setTimeout,您将引入带有后续更新的竞争条件。
    • 我对延迟加载图像也使用了同样的技术。如果我直接在 componentDidMount 中调用我的lazyLoadImages 函数,则位于浏览器选项卡中的页面加载指示器将继续旋转,直到所有图像都加载完毕。但是如果将lazyLoadImages函数用100ms的参数包裹在setTimeout中,情况就不同了。当图像开始在后台加载时,加载指示器将很快停止旋转。 setTimeout 让体验更好。将延迟加载函数绑定到窗口 onload 事件也是如此,但它不适用于 SPA。
    【解决方案6】:

    在现代浏览器中,应该是这样的

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

    【讨论】:

    • 呵呵,除了现代浏览器不使用 jquery =P。不过,如果它应该是实时更新的,那么使用动画帧是一个很好的提示。
    【解决方案7】:

    您可以使用useRef 挂钩来查看您的容器元素。 注意,需要特别注意refcurrent值,否则不起作用。

    例子:

      const containerRef = useRef();
      const { current } = containerRef;
    
      useEffect(setLinksData, [current]);
    
    return (
        <div ref={containerRef}>
          // your child elements...
        </div>
    )
    

    【讨论】:

      【解决方案8】:

      下面是我在尝试使用 document.getElementsByClassName 获取类之前要等待 DOM 准备好的方法。我从 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);
                  }
           }
      

      【讨论】:

        【解决方案9】:

        似乎对我有用

        const [isLoaded, setIsLoaded] = useState(false);
        const [isPageLoaded, setIsPageLoaded] = useState(false); //this helps
        
        useEffect(() => {
            setIsLoaded(true);
        }, []);
        
        useEffect(() => {
            if (isLoaded) {
                setIsPageLoaded(true);
            }
        }, [isLoaded]);
        

        【讨论】:

        • 这可以工作,但链接两个 useStates 和两个 useEffects 只是为了检查页面是否已加载是一场性能噩梦。
        • 是的,在我的情况下,页面不重并且可以正常工作,但真正的解决方案是什么?我很确定有东西,但还是找不到!
        • 老实说,我自己一直在寻找答案。看起来 React 18 会带来开箱即用的延迟加载。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        • 2016-07-23
        • 1970-01-01
        • 2011-05-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多