useState set 方法没有立即反映更改
我正在尝试学习钩子,而 useState 方法让我感到困惑。我正在以数组的形式为状态分配一个初始值。 useState 中的 set 方法对我来说不起作用,无论有没有扩展语法。
我在另一台 PC 上创建了一个 API,我正在调用它并获取我想要设置为状态的数据。
这是我的代码:
<div id="root"></div>
<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant
const StateSelector = () => {
const initialValue = [
{
category: "",
photo: "",
description: "",
id: 0,
name: "",
rating: 0
}
];
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
// const response = await fetch("http://192.168.1.164:5000/movies/display");
// const json = await response.json();
// const result = json.data.result;
const result = [
{
category: "cat1",
description: "desc1",
id: "1546514491119",
name: "randomname2",
photo: null,
rating: "3"
},
{
category: "cat2",
description: "desc1",
id: "1546837819818",
name: "randomname1",
rating: "5"
}
];
console.log("result =", result);
setMovies(result);
console.log("movies =", movies);
} catch (e) {
console.error(e);
}
})();
}, []);
return <p>hello</p>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
setMovies(result)
和 setMovies(...result)
都不起作用。
我希望将 result
变量推入 movies
数组中。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
没有任何附加的NPM软件包
Without any addtional NPM package
⚠️功能组件
我认为,一种超级干净的方法是创建一个自定义挂钩,它提供了将回调传回设置器函数的能力,那么,在此之后精确地采取某些操作将是100%保证国家的更新。
通过查看这篇文章您可以理解如何制作
usestatecallback
hook。通过使用usestatecallback
来定义状态,就像以下内容:⚠️ Functional Components
I believe a super clean way would be to create a custom hook that provides the ability to pass a callback to the setter function, then it would be a 100% guarantee to do some actions exactly after the update of the state.
By taking a look at this post you can understand how to make the
useStateCallback
hook. Defining a state by using theuseStateCallback
would be like the following:问题
我的问题并没有真正试图在调用设定方法后立即访问该州。在Rerender发生后,我试图以完全不同的函数来执行此操作,但是更新仍未得到反映。目标函数是在功能组件中定义的,但是从类组件中调用的。
就我而言,我最终意识到这是由陈旧关闭引起的问题。这可能是因为类组件不使用
usestate
功能,因此我的代码中的类组件将函数传递给了它并创建了它或其他东西,并且该副本并未使用最多的副本 - 至今引用我的变量。直接传递给类组件的实际变量仍然适当地反映了。用功能组件代替类组件的解决方案
为我解决了问题。
Issue
My issue wasn't really trying to access the state right away after calling the set method. I was attempting to do it in a completely different function after the rerender had happened but the update still wasn't being reflected. The target function was defined in a functional component but it was being called from a class component.
In my case, I ended up realizing that it was an issue caused by a stale closure. This was likely happening because class components do not use
useState
functionality and so the class component in my code took the function passed to it and created a copy of it or something and that copy was not using the most up-to-date reference to my variable. Actual variables passed directly to the class component still got reflected properly though.Solution
Replacing the class component with a functional component solved the issue for me.
您可以尝试将使用效果挂钩与包含iSopen的依赖项数组一起使用,每次iSopen状态更改时都会执行回调函数,从而确保Console.log(Isopen)记录更新的状态值。这样,您可以正确处理状态更新并确保UI反映组件的当前状态。
You can try to use the useEffect hook with a dependency array containing isOpen, the callback function will be executed every time the isOpen state changes, ensuring that console.log(isOpen) logs the updated state value. This way, you can correctly handle state updates and ensure that your UI reflects the current state of your component.
并不是说要这样做,但是在没有使用效果的情况下做OP要求的事情并不难。
使用承诺在设置器函数的正文中解析新状态:
这就是您的使用方式(示例显示了
Count
和OUTOFSYNCCOUNT
/之间的比较UI渲染中的Synccount
):Not saying to do this, but it isn't hard to do what the OP asked without useEffect.
Use a promise to resolve the new state in the body of the setter function:
And this is how you use it (example shows the comparison between
count
andoutOfSyncCount
/syncCount
in the UI rendering):现在,您应该看到,您的代码实际上确实有效。不起作用的是
console.log(Mission)
。这是因为电影
指向旧状态。如果您将console.log(电影)
移动使用
,就在返回上方,您将看到更新的电影对象。Now you should see, that your code actually does work. What does not work is the
console.log(movies)
. This is becausemovies
points to the old state. If you move yourconsole.log(movies)
outside ofuseEffect
, right above the return, you will see the updated movies object.使用后台计时器库。它解决了我的问题。
Use the Background Timer library. It solved my problem.
类似于通过扩展
React.Component< 创建的类组件中的
.setState()
/code> 或
React.PureComponent
,使用useState
hook 提供的更新器进行状态更新也是异步的,不会立即反映。此外,这里的主要问题不仅仅是异步性质,而是函数根据当前闭包使用状态值,并且状态更新将反映在下一次重新渲染中,现有闭包不会受到影响,但会创建新的。现在在当前状态下,钩子内的值是通过现有的闭包获取的,当重新渲染发生时,闭包会根据函数是否再次重新创建而更新。
即使您添加
setTimeout
函数,尽管超时将在重新渲染发生一段时间后运行,setTimeout
仍将使用其值之前的关闭而不是更新的关闭。如果要对状态更新执行操作,则需要使用
useEffect
钩子,就像在类组件中使用componentDidUpdate
一样,因为 setter 由useState< 返回/code> 没有回调模式
就更新状态的语法而言,
setMovies(result)
会将状态中之前的movies
值替换为这些值可从异步请求中获取。但是,如果要将响应与先前存在的值合并,则必须使用状态更新的回调语法以及正确使用扩展语法,例如
Much like
.setState()
in class components created by extendingReact.Component
orReact.PureComponent
, the state update using the updater provided byuseState
hook is also asynchronous, and will not be reflected immediately.Also, the main issue here is not just the asynchronous nature but the fact that state values are used by functions based on their current closures, and state updates will reflect in the next re-render by which the existing closures are not affected, but new ones are created. Now in the current state, the values within hooks are obtained by existing closures, and when a re-render happens, the closures are updated based on whether the function is recreated again or not.
Even if you add a
setTimeout
the function, though the timeout will run after some time by which the re-render would have happened, thesetTimeout
will still use the value from its previous closure and not the updated one.If you want to perform an action on state update, you need to use the
useEffect
hook, much like usingcomponentDidUpdate
in class components since the setter returned byuseState
doesn't have a callback patternAs far as the syntax to update state is concerned,
setMovies(result)
will replace the previousmovies
value in the state with those available from the async request.However, if you want to merge the response with the previously existing values, you must use the callback syntax of state updation along with the correct use of spread syntax like
上一个答案::
时SetState 是异步(包括类和钩子),很容易使用该事实来解释观察到的行为,不是 的原因 发生。
tldr:原因是封闭> const 值。
解决方案:
读取渲染函数中的值(不在嵌套功能内):
将变量添加到依赖项中(并使用 react-hooks/详尽的deps eslint规则):
使用临时变量:
使用可变的参考(如果我们不需要状态并且只想记住值 - 更新ref不会触发重新渲染):
说明为什么会发生:
如果async是唯一的原因,可以
等待SetState()
。但是,这两个
props
和状态
均为假定在1渲染期间不变。使用挂钩,通过使用常数值与
const
关键字:该值在2个渲染之间可能有所不同,但是在2个渲染之间可能有所不同,但是在渲染本身和任何任何内部都保持常数闭合>
使用效果
,事件处理程序,在任何承诺或settimeout中)。考虑遵循伪造的,但同步,类似反应的实现:
Additional details to the previous answer:
While React's
setState
is asynchronous (both classes and hooks), and it's tempting to use that fact to explain the observed behavior, it is not the reason why it happens.TLDR: The reason is a closure scope around an immutable
const
value.Solutions:
read the value in render function (not inside nested functions):
add the variable into dependencies (and use the react-hooks/exhaustive-deps eslint rule):
use a temporary variable:
use a mutable reference (if we don't need a state and only want to remember the value - updating a ref doesn't trigger re-render):
Explanation why it happens:
If async was the only reason, it would be possible to
await setState()
.However, both
props
andstate
are assumed to be unchanging during 1 render.With hooks, this assumption is enhanced by using constant values with the
const
keyword:The value might be different between 2 renders, but remains a constant inside the render itself and inside any closures (functions that live longer even after render is finished, e.g.
useEffect
, event handlers, inside any Promise or setTimeout).Consider following fake, but synchronous, React-like implementation:
我知道已经有很好的答案了。但是我想给出另一个想法,如何解决同一问题,并使用我的模块 react-usestateref 每周下载11,000多个。
正如您通过使用React状态所理解的那样,您可以每次变化时都会渲染页面。但是,通过使用React Ref,您始终可以获取最新值。
因此,模块
react-usestateref
让您一起使用状态和ref。它向后与react.usestate
兼容,因此您可以替换import
语句更多信息:
I know that there are already very good answers. But I want to give another idea how to solve the same issue, and access the latest 'movie' state, using my module react-useStateRef it has 11,000+ weekly downloads.
As you understand by using React state you can render the page every time the state change. But by using React ref, you can always get the latest values.
So the module
react-useStateRef
let you use state's and ref's together. It's backward compatible withReact.useState
, so you can just replace theimport
statementMore information:
这里的大多数答案都是关于如何根据其先前的值更新状态,但我不明白这与问题有何关系
React 18
useState 是异步的:
当触发特定代码的事件发生时,代码开始运行,当它完成时,react 将检查是否有状态更新以及是否有状态更新在这种情况下,只有
useState
挂钩的值才会更新,这会导致新的渲染,其中新值可用。假设我们有一个场景我们有一个国家这取决于另一个状态,例如我们希望在每次更新时根据
example
的新值进行 API 调用,然后将响应中的数据存储在另一个状态anotherExample.
所以我们有两种方法来实现:
1.使用
newValue
的值:因为您知道
example
将接收该值,所以您可以直接基于它创建逻辑。2.每次更新
example
时触发 useEffect 运行,包括example
在其依赖数组中:因此,当
example
使用组件重新渲染的事件函数更新时,我们现在处于一个新的不同状态渲染完成后,useEffect
将运行因为example
的值与上次渲染期间的值不同,而且由于它是一个新的不同渲染,因此example
useState 挂钩的新值可在此处获得。注意:
useEffect
钩子无论如何都会在第一次挂载期间运行。哪种方法更好?
虽然第一种方法将使所有工作在一次渲染中完成
Most of the answers here are about how to update a state based on its previous value, but I don't understand how that relates to the question
React 18
useState is asynchronous:
When an event that triggers a certain code, occurs, the code starts running, and when it finshes, react will check if there was a state update and if it is the case, only then the value of the
useState
hook is updated and this leads to a new render in which the new value is availabe.Supposing we have a scenario where we have a state which depends on another state, for example we want to make an API call based on the new value of
example
every time it is updated and then store the data from response in another stateanotherExample
.to achieve so we have two ways:
1. use the value of
newValue
:since you know that
example
will receive this value, you can create your logic based on it directly.2. trigger a useEffect to run each time
example
is updated by includingexample
in its dependency array:so when
example
is updated with the event function the component rerenders, we are now in a new different render that once finished,useEffect
will run because the value ofexample
is different from what is was during the last render, and since it is a new different render, the new value ofexample
useState hook is available here.Note: the
useEffect
hook will run anyway during the first mount.Which approach better?
while the first method will make all the work in one render ???? (a better approach) "React groups multiple state updates into a single re-render for better performance" the second method will do it in two renders, the first when
example
is updated and the second whenanotherExample
is updated from insideuseEffect
????since the component only rerenders when the new value of a
useState
hook is different from the old one, so whennewValue
is equal toexample
the component will not rerender so theuseEffect
will not run andanotherExample
will not be updated ???? (a better approach), however in the first method the API is called anyway and we don't want to do that if there is no need also if this happensanotherExample
will be updated (anotherExample
will receive the same data it already contains because it is the same REQUEST sincenewValue
is equal toexample
) but if the response in an object or an array then,Object.is
method (that theuseState
hook utilizezs), cannot detect if the new value is equal to the previous one, therefore, the component will rerender ????Conclusion:
As it is mentioned above, each one has its advantage, so it depends on the use case.
the second method is more recommended, however the first can be more performant in some cases, for example when you are sure the code will only run when
newValue
gets a new value usingonChange
, or maybe when you want to use some other local variables that you will no longer have access to from inside useEffect正如上面的其他答案已经澄清了这里的错误,即
useState
是异步的,并且您试图使用setState
之后的值。由于setState
的异步特性,它不会在console.log()
部分更新,它允许您执行进一步的代码,而值更新发生在后台。这样你就得到了之前的值。当setState
在后台完成时,它将更新该值,您将可以在下一次渲染时访问该值。如果有人有兴趣详细了解这一点。这是关于该主题的非常好的会议演讲。
https://www.youtube.com/watch?v=8aGhZQkoFbQ
As other answers above have clarified the error here, which is that
useState
is asynchronous and you are trying to use the value just aftersetState
. It is not updating on theconsole.log()
part because of the asynchronous nature ofsetState
, it lets your further code to execute, while the value updating happens on the background. Thus you are getting the previous value. When thesetState
is completed on the background it will update the value and you will have access to that value on the next render.If anyone is interested to understand this in detail. Here is a really good Conference talk on the topic.
https://www.youtube.com/watch?v=8aGhZQkoFbQ
React的使用效应具有自己的状态/生命周期。它与状态突变有关,直到效果破坏之前,它不会更新状态。
只需将单个参数传递在使用效率参数数组中的参数或留空时,它将完美地工作。
另外,您可以尝试react.useref()以立即更改React钩子。
React's useEffect has its own state/lifecycle. It's related to mutation of state, and it will not update the state until the effect is destroyed.
Just pass a single argument in parameters in useEffect params array or leave it a blank, it will work perfectly.
Alternatively, you can try React.useRef() for instant change in the React hook.
在肯特·C·多德斯(Kent C. Dodds)的文章(参考文献)之后,我刚刚完成了与用户介绍者的重写,这确实给了我可靠的结果,从这些封闭问题中却没有一点。
请参阅:
我将他可读的样板凝结为我首选的干燥水平 - 阅读他的沙盒实现将向您展示其实际工作方式。
使用类似的用法:
现在,我所有页面上到处的一切都持续下去
I just finished a rewrite with useReducer, following the Kent C. Dodds article (ref below) which really gave me a solid result that suffers not one bit from these closure problems.
See: https://kentcdodds.com/blog/how-to-use-react-context-effectively
I condensed his readable boilerplate to my preferred level of DRYness -- reading his sandbox implementation will show you how it actually works.
With a usage similar to this:
Now everything persists smoothly everywhere across all my pages
关闭不是唯一的原因。
基于
usestate
的源代码(下面简化)。在我看来,这个价值从未立即分配。发生的事情是,当您调用
setValue
时,更新操作将排队。在时间表开始之后,只有当您进入下一个渲染时,这些更新操作才会应用于该状态。这意味着即使我们没有关闭问题,
usestate
的React版本也不会立即为您提供新的值。直到下一个渲染之前,新值甚至不存在。还有一篇文章以类似的方式解释上述内容, https://dev.to/adamklein/we-don-t-know-how-now-now-now-now-state-hook-works-works-1lp8
The closure is not the only reason.
Based on the source code of
useState
(simplified below). Seems to me the value is never assigned right away.What happens is that an update action is queued when you invoke
setValue
. And after the schedule kicks in and only when you get to the next render, these update action then is applied to that state.Which means even we don't have closure issue, react version of
useState
is not going to give you the new value right away. The new value doesn't even exist until next render.There's also an article explaining the above in the similar way, https://dev.to/adamklein/we-don-t-know-how-react-state-hook-works-1lp8
我发现这很好。它没有定义状态(方法1)为例,
请尝试此方法(方法2),
而是在不使用使用效果的情况下解决了果兰德问题,因为我们不关心其内部封闭方法与这种情况。
PS:如果您关心在任何用例中使用旧状态,则需要使用使用效果的Usestate,因为它需要具有该状态,因此在这种情况下应使用方法1。
I found this to be good. Instead of defining state (approach 1) as, example,
Try this approach (approach 2),
This resolved the rerender issue without using useEffect since we are not concerned with its internal closure approach with this case.
P.S.: If you are concerned with using old state for any use case then useState with useEffect needs to be used since it will need to have that state, so approach 1 shall be used in this situation.
如果我们只需更新状态,那么更好的方法是使用推送方法来执行此操作。
这是我的代码。我想在状态中存储来自 Firebase 的网址。
此代码用于将 URL 置于数组状态。这可能也适合你。
If we have to update state only, then a better way can be if we use the push method to do so.
Here is my code. I want to store URLs from Firebase in state.
This code works to put URLs in state as an array. This might also work for you.
我想补充@aprillion 和@shubham-khatri 的建议。
@shubham-khatri 建议这样做:
对吗?
但我的代码是这样的(
myfunction
在点击事件上触发)上面的代码仍然不起作用。请注意区别,区别在于我有异步。如果您有
async
,则无论[movies]
如何,代码都不会等待更新的movies
,因此您必须这样做:并且调用代码从
onClick={myfunction}
更改为onClick={() =>; myfunction(电影)}
I would like to add to what @aprillion and @shubham-khatri suggested.
@shubham-khatri suggested this:
right ?
But my code was like this (
myfunction
being triggered on a click event)This above code was still not working. Note the difference, the difference is I have
async
. In case you haveasync
the code will not wait for updatedmovies
irrespective of[movies]
, so you will have to do like this:and the calling code changed from
onClick={myfunction}
toonClick={() => myfunction(movies)}
由于usestate是异步直接替换状态不是一个好主意。因此,最好访问先前的状态并设置新的更新值。
for ex:setState(((prev)=&gt; [... prev,... result]);
//结果是对象数组
Since useState is async direct replacement of state is not a good idea. so it is better to access the previous state and set new updated value.
for ex: setState((prev)=>[...prev, ...result]);
//result is array of objects
使用我的库中的自定义挂钩,您可以等待状态值更新:
useAsyncWatcher(...values):watcherFn(peekPrevValue: boolean)=>Promise
- 是 useEffect 的承诺包装器,如果可选的 peekPrevValue 参数设置为 true,则可以等待更新并返回一个新值,也可能返回前一个值。(现场演示)
useAsyncDeepState
是一个深层状态实现(类似于 this.setState (patchObject)),其 setter 可以返回与内部效果同步的 Promise。如果不带参数调用 setter,它不会更改状态值,而只是订阅状态更新。在这种情况下,您可以从组件内的任何位置获取状态值,因为函数闭包不再是障碍。(现场演示)
With custom hooks from my library, you can wait for the state values to update:
useAsyncWatcher(...values):watcherFn(peekPrevValue: boolean)=>Promise
- is a promise wrapper around useEffect that can wait for updates and return a new value and possibly a previous one if the optionalpeekPrevValue
argument is set to true.(Live Demo)
useAsyncDeepState
is a deep state implementation (similar to this.setState (patchObject)) whose setter can return a promise synchronized with the internal effect. If the setter is called with no arguments, it does not change the state values, but simply subscribes to state updates. In this case, you can get the state value from anywhere inside your component, since function closures are no longer a hindrance.(Live Demo)