英文:
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代码执行顺序如下:
useEffect
钩子- 渲染按钮
而我想要的是(根据React的设计应该是):
- 渲染按钮
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(()=> {
/* doing a bunch of non-related stuff here. */
setLoading(false)
console.log("setLoading to false.")
}, [])
useEffect(() => {
if(loading == false)
{
console.log("Loading verified to be false");
SpatialNavigation.focus(".onboarding-button-welcome") // this highlights the button.
}
},[loading])
return (
<button className="onboarding-button-welcome"> Get Started </button>
)
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:
- useEffect hooks
- Rendering the button
and I want it to be (the way it SHOULD be according to React):
- Rendering the button
- 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 'react';
function MyComponent() {
const [loading, setLoading] = useState(true);
const buttonRef = useRef(null);
useEffect(() => {
/* doing a bunch of non-related stuff here. */
setLoading(false);
console.log("setLoading to false.");
}, []);
useLayoutEffect(() => {
if (!loading && buttonRef.current) {
console.log("Button element exists in the DOM, focusing...");
buttonRef.current.focus();
}
}, [loading]);
return (
<button className="onboarding-button-welcome" ref={buttonRef}>
Get Started
</button>
);
}
答案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 useEffect
s are going to be invoked near instantaneously.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论