useEffect钩子在渲染完成之前已经完全执行完成

huangapple go评论66阅读模式
英文:

useEffect hooks are running to completion BEFORE rendering is completed

问题

我非常惊讶地发现在Stack Overflow上找不到更多关于这个问题的帖子。关于在React中使用useEffect钩子的所有内容都表明它应该在渲染和重新渲染完成后才运行,但我的钩子却完全相反,让我非常沮丧。

在我看到屏幕上任何内容呈现之前,它们都会全部运行完毕,这与useEffect钩子应该做的事情完全相反。

我在这里尝试做的只是在页面加载时突出显示/聚焦一个按钮。

以下是我useEffect代码的一个非常简化的版本。

const [loading, setLoading] = useState(true)

useEffect(() => {

    /* 在这里进行了一堆与主题无关的操作。 */

    setLoading(false)
    console.log("将loading设置为false。")

    
}, [])

useEffect(() => {
    if(loading === false)
    {
        console.log("已验证loading为false");
        SpatialNavigation.focus(".onboarding-button-welcome") // 这会突出显示按钮。
    }
}, [loading])

return (
 <button className="onboarding-button-welcome">开始</button>
)

如您所见,loading 状态变量在初始渲染时开始为true,然后应该由上面的 useEffect 更改为false,这会导致下面的 useEffect 被触发,检查 loading 状态变量是否为false,然后聚焦按钮。

我的问题(我已经用调试器检查了无数次)是上述所有操作都发生在我在屏幕上看到任何内容之前。在所有代码运行完毕之前我看不到按钮。

useEffect 钩子完成,然后才是我的渲染完成,这意味着我的 SpatialNavigation.focus() 方法在DOM中找不到任何要聚焦的按钮,这意味着页面加载时按钮没有被突出显示。在所有我的 useEffect 钩子运行之前,我从不在屏幕上看到任何东西,这是一个重大问题,因为......

关于 useEffect 钩子的所有帖子都说这种功能应该是不可能的,因为它们只应该在渲染完成后和屏幕上有内容时才触发,所以这里出了什么问题?正如我所说,我有一种经过验证的方法来解决这个问题,尽管有点笨拙,那就是将 SpatialNavigation.focus() 行包装在一个 setTimeout 函数中,但我宁愿不这样做。我只是希望渲染和钩子按照它们应该触发的顺序触发。

简而言之

我的简单的React代码执行顺序如下:

  1. useEffect 钩子
  2. 渲染按钮

而我想要的是(根据React的设计应该是):

  1. 渲染按钮
  2. useEffect 钩子
英文:

I am utterly astounded that I haven't been able to find more posts about this on stack overflow. Everything about a useEffect hook in react reads that it's supposed to run only after rendering and re-rendering has finished but my hooks are doing the EXACT opposite thing, much to my frustration.

They are all running to completion before I see anything render on my screen whatsoever which goes against everything useEffect hooks are supposed to do.

All I'm trying to do here is have a single button be highlighted/focused when the page loads in.

Here's a very dumbed down version of my useEffect code.

const [loading, setLoading] = useState(true)

useEffect(()=&gt; {

    /* doing a bunch of non-related stuff here. */

    setLoading(false)
    console.log(&quot;setLoading to false.&quot;)

    
}, [])

useEffect(() =&gt; {
    if(loading == false)
    {
        console.log(&quot;Loading verified to be false&quot;);
        SpatialNavigation.focus(&quot;.onboarding-button-welcome&quot;) // this highlights the button.
    }
},[loading])

return (
 &lt;button className=&quot;onboarding-button-welcome&quot;&gt; Get Started &lt;/button&gt;
)

As you can see, the loading state variable starts as true for the initial render and is then supposed to then be changed to false by the upper useEffect, which causes the lower useEffect to fire, check that the loading state variable is false, then focus the button.

My problem (and I've checked this a thousand times with a debugger) is that all of the above is happening before I see anything appear on my screen. I see no button until after all the code has run.

The useEffect hooks are completing and THEN my render is completing, which means that my SpatialNavigation.focus() method is not finding any button to focus in the DOM, which means that the page loads in without the button being highlighted. I never see anything on my screen until all my useEffect hooks have run and that's a major problem because well....

Everything posted everywhere about useEffect hooks says this functionality should be impossible since they are only supposed to fire AFTER rendering has completed and the screen has content so what gives here? Like I said, I'm kinda shocked that I haven't seen this question posted on stack overflow because it's a pretty glaring issue as far as React's functionality is concerned.

I should note that I do have a tried and proven albeit hacky way to fix this by wrapping the SpatialNavigation.focus() line in a setTimeout function but I'd rather not have to do that. I just want the rendering and hooks to trigger in the order that they should be triggering.

TL;DR

My simple React code's execution order is somehow:

  1. useEffect hooks
  2. Rendering the button

and I want it to be (the way it SHOULD be according to React):

  1. Rendering the button
  2. useEffect hooks

答案1

得分: 1

React中的useEffect钩子在渲染后运行但不能保证在执行之前渲染已完成如果需要与渲染元素交互请使用useRef来确保它在DOM中可用

import { useEffect, useLayoutEffect, useRef, useState } from 'react';

function MyComponent() {
  const [loading, setLoading] = useState(true);
  const buttonRef = useRef(null);

  useEffect(() => {
    /* 在这里执行一堆与渲染无关的操作。 */

    setLoading(false);
    console.log("将loading设置为false。");
  }, []);

  useLayoutEffect(() => {
    if (!loading && buttonRef.current) {
      console.log("DOM中存在按钮元素,正在聚焦...");
      buttonRef.current.focus();
    }
  }, [loading]);

  return (
    <button className="onboarding-button-welcome" ref={buttonRef}>
      Get Started
    </button>
  );
}
英文:

The useEffect hook in React runs after rendering but does not guarantee that rendering is completed before its execution. If you need to interact with a rendered element, use useRef to ensure it's available in the DOM.

   import { useEffect, useLayoutEffect, useRef, useState } from &#39;react&#39;;

function MyComponent() {
  const [loading, setLoading] = useState(true);
  const buttonRef = useRef(null);

  useEffect(() =&gt; {
    /* doing a bunch of non-related stuff here. */

    setLoading(false);
    console.log(&quot;setLoading to false.&quot;);
  }, []);

  useLayoutEffect(() =&gt; {
    if (!loading &amp;&amp; buttonRef.current) {
      console.log(&quot;Button element exists in the DOM, focusing...&quot;);
      buttonRef.current.focus();
    }
  }, [loading]);

  return (
    &lt;button className=&quot;onboarding-button-welcome&quot; ref={buttonRef}&gt;
      Get Started
    &lt;/button&gt;
  );
}

答案2

得分: 0

这是因为React会非常快地调用两个useEffect,所以这发生在你在屏幕上看到任何东西之前。

setTimeout似乎非常适合你的用例。

除非你实际上在等待异步操作的响应,否则两个useEffect都有很高的机会几乎同时被调用。

英文:

Its happening before you see anything on the screen because react invokes both useEffect's very very quickly.

setTimeout feels like a good fit for your use case.

Unless you're actually waiting for a response from an async operation, chances are high that both useEffects are going to be invoked near instantaneously.

huangapple
  • 本文由 发表于 2023年8月4日 03:56:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76831271.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定