【问题标题】:Why useEffect hook runs before the DOM is loaded?为什么 useEffect 挂钩在 DOM 加载之前运行?
【发布时间】:2019-12-10 14:21:26
【问题描述】:

我无法从 react 的 useEffect Hook 访问元素。

const Guest = () => {

    useEffect(() => {

        let selectedElement = document.body.querySelectorAll('[contenteditable=true]');
        console.log(selectedElement)

    })

    return (
        <Fragment>
           <div id='guest-editorjs'>
              <Editor
                  tools={{
                     header: Header,
                     list: List,
                     image: Image,
                     delimiter: Delimiter
                  }}
                  holder="guest-editorjs"
                  onChange={(data) => console.log(data)}
                  onReady={() => console.log('Start!')}
                 data={data} />
        </div>
      </Fragment>

}

我正在尝试访问 contenteditable=true 的元素,并且所有这些元素都包含在此编辑器组件中。

但是当我运行代码时,控制台输出返回 console.log(selectedElement)(结果:空 NodeList),然后来自编辑器组件的 onReady 的 console.log('Start!')

但是 useEffect 仅在渲染完成后才开始工作,还是我在某些时候错了?

更新:Guest组件第一次渲染后输出为: - 空节点列表 [] - 开始!

但是当我重新渲染组件时,它会返回包含所有需要元素的 NodeList。

【问题讨论】:

  • 在功能组件中可以使用 useEffect ,因为其中没有生命周期挂钩。 useEffect 将始终在组件 dom 加载之前运行。Check this doc and the TIP
  • useEffect 没有依赖关系意味着componentDidUpdatecomponentDidMount。如果您希望稍后再使用,请使用useEffect(function, [])ref
  • @Rajesh,我尝试通过 [] 但结果相同。 React 有没有办法在加载 DOM 时先等待,然后才调用函数?
  • 我想我在尝试scrollIntoView 时遇到了同样的问题。它不适用于初始页面渲染。效果确实运行了,但元素报告的大小为零,因此滚动不起作用。在随后的渲染中,它确实运行良好。我使用[] 依赖项添加了一个特殊效果,它运行scrollIntoView 作为对window.addEventListener('load',...) 的回调,现在它可以工作了!我需要两种效果才能基本上做同样的事情,这有点令人沮丧:(

标签: javascript reactjs


【解决方案1】:

我也遇到过这个问题,我的解决方案是创建内部状态,在重新渲染 DOM 时设置,看下面的代码来理解:

const [rerender, setRerender] = useState(); // or any state
const [afterRender, setAfterRender] = useState();// internal state

// (1)
useEffect(() => {
   if (!afterRender) return;
   // here DOM is loaded and you can query DOM elements
   // then reset
   setAfterRender(false);
}, [afterRender]);

useEffect(() => {
   setAfterRender(true); // (1) will be called after DOM rendered
}, [rerender]); // or don't set any if you want to listen to all re-render events

return {
  setRerender, // expose function trigger re-render data
}

【讨论】:

  • 如果返回有对象return {setRerender} 那么我们将把我们的HTML/组件放在哪里呢?即return (&lt;Component&gt;&lt;i&gt;Hello World&lt;/i&gt;&lt;/Component)
  • @ImranBughio 将此代码放入 react 自定义挂钩并重用它。
【解决方案2】:

2021 年更新(React 版本 17.0.2)

useEffect 将在 DOM 加载和 UI 绘制之后运行(只是 useLayoutEffect 在 UI 绘制之前运行)。

按顺序运行:

  1. DOM 操作。
  2. 使用布局效果
  3. UI 绘制
  4. 使用效果

它适用于 componentDidMount 和 componentDidUpdate 生命周期。

【讨论】:

    【解决方案3】:

    如果您需要访问 DOM,可以改用 useLayoutEffect

    https://reactjs.org/docs/hooks-reference.html#uselayouteffect

    【讨论】:

    • 面临与 OP 相同的问题。 useLayoutEffect 没有解决它。 useLayoutEffect 甚至比useEffect 运行得更早,因为它是为了防止闪烁效果,我没有正确理解吗?
    • @vicmortelmans 根据我链接到useLayoutEffect 的文档,它与componentDidUpdate 在同一阶段运行。所以渲染应该在钩子被触发之前发生。它还特别提到了对 DOM 的访问。
    • @vicmortelmans 我认为它在 dom 被创建和被“显示”之间运行?
    【解决方案4】:

    至于 React 版本

    可以通过从onLoad 事件调用回调来解决这种情况

    但我认为它只适用于加载的第一个 DOM。

    const App = () => {
        const handleLoaded = () => {
            // action here
        };
    
        return <div onLoad={handleLoaded}>{/* child here */}</div>;
    };
    
    

    【讨论】:

      猜你喜欢
      • 2020-10-17
      • 2022-10-09
      • 1970-01-01
      • 2021-09-07
      • 2020-09-07
      • 2021-07-02
      • 2023-02-14
      • 2020-12-06
      • 1970-01-01
      相关资源
      最近更新 更多