reactjs useState()undefined in React just after set

jfgube3f  于 2023-04-20  发布在  React
关注(0)|答案(8)|浏览(167)

我有一个问题,我不明白为什么钩子返回undefined:

import React, { useEffect, useState } from 'react';

function App(){

    const [globalVariable, setGlobalVariable] = useState();

  useEffect(()=>{

    const test = 5
    console.log(test) // return 5
    setGlobalVariable(test)
    console.log(globalVariable) // return undefined
    
    
    },[]);
  
  

  return (
    <div>
    </div>
  );
}

export default App;

我该怎么做才能直接为globalVariable设置一个新值?

tnkciper

tnkciper1#

正如其他成员所提到的,setState在基于队列的系统中异步运行。这意味着如果你想执行状态更新,这个更新将堆积在队列上。但是,它不会返回Promise,所以你不能使用.thenwait。正因为如此,我们需要意识到在React中操作状态时可能出现的问题。
假设我们有:

// PROBLEM # 1
export function Button() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount(count + 1);
    console.log(count); // returns 0
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

由于setCount是异步的,console.log将返回更新之前的值(这里是0)。在您的情况下,它将返回undefined,因为您没有在useState钩子中传递任何初始状态。您所做的另一件不同的事情是从useEffect钩子内部触发状态更改,但这对眼前的问题来说并不重要
要解决这个问题,一个好的做法是将新状态存储在另一个变量中,如下所示:

// SOLUTION TO PROBLEM # 1
export function Button() {
  const [count, setCount] = useState(0);

  function increment() {
    const newCountValue = count + 1;

    setCount(newCountValue);
    console.log(newCountValue); // returns 1
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

这基本上回答了你的问题。
但是,还有其他情况下,状态更新可能不会像预期的那样。重要的是要注意这些情况,以避免可能的错误。例如,让我们假设我们连续多次更新给定的状态,如:

// PROBLEM # 2
export function Button() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
    console.log(count); // returns 0, but the final state will be 1 (not 3)
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

这里,console.log仍然返回0,原因与问题1中描述的相同。但是,如果您在应用中的某个位置使用count状态,您将看到onClick事件后的最终值为1,而不是3。
以下是如何解决此问题:

// SOLUTION TO PROBLEM # 2
export function Button() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount((oldState) => oldState + 1);
    setCount((oldState) => oldState + 1);
    setCount((oldState) => oldState + 1);
    console.log(count); // returns 0, but the final state will be 3
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

通过向setState传递一个函数,使用队列上的前一个值而不是初始值来计算新状态。
作为最后一个例子,假设我们在尝试改变状态之后调用了一个函数,但是这个函数依赖于状态值。由于操作是异步的,所以这个函数将使用“旧”的状态值,就像问题#1一样:

// PROBLEM # 3
export function Button() {
  const [count, setCount] = useState(0);

  onCountChange() {
    console.log(count); // returns 0;
  }

  function increment() {
    setCount(count + 1);
    onCountChange();
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

注意在这个问题中,我们有一个依赖于外部变量的函数onCountChangecount)。所谓外部,我的意思是这个变量没有在onCountChange的作用域内声明。它们实际上是兄弟。(两者都位于Button范围内)。现在,如果你熟悉“纯函数”、“幂等”和“副作用”的概念,您将意识到,我们在这里遇到的是一个副作用问题--我们希望在状态更改之后发生一些事情。
粗略地说,每当你需要在React中处理一些副作用时,这都是通过useEffect钩子来完成的。所以,要解决我们的第三个问题,我们可以简单地这样做:

// SOLUTION TO PROBLEM # 3
export function Button() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(count); // returns 0;
  }, [count]);

  function increment() {
    setCount(count + 1);
  }

  return (
    <button onClick={increment}>Increment<\button>
  )
}

在上面的解决方案中,我们将onCountChange的内容移动到useEffect,并添加count作为依赖项,因此每当它发生变化时,效果就会被触发。

r7knjye2

r7knjye22#

两件事:

  • useState有一个初始值,默认为undefined。
const [globalVariable, setGlobalVariable] = useState(123); // initial value set to 123
  • setGlobalVariable不是同步操作。更改它不会立即改变globalVariable。作用域中的值将保持不变,直到下一个渲染阶段。
6ju8rftf

6ju8rftf3#

因为state只有在组件重新渲染时才有新的值。所以只需要像这样将console.log(globalVariable)放在useEffect之外:

useEffect(() => {
  const test = 5;
  console.log(test); // return 5
  setGlobalVariable(test);
}, []);
console.log(globalVariable);
xam8gpfp

xam8gpfp4#

这是因为组件没有重新渲染。你需要在渲染之间保持值,为此你需要使用useState将变量更改为状态。为了更好地理解,请查看下面的代码:

import React, { useEffect, useState } from "react";

function Test() {
  const [globalVariable, setGlobalVariable] = useState();
  const [test, setTest] = useState(5);

  useEffect(() => {
    // const test = 5;
    // console.log(test); // return 5
    setGlobalVariable(test);
    console.log(globalVariable); // return undefined
    console.log(test); // return 5
  }, []);

  return <div></div>;
}

export default Test;
6ie5vjzr

6ie5vjzr5#

1.这是因为React状态更新是基于队列的系统。它不会立即更新。但是如果你渲染一个全局变量值,你会得到所需的值。
1.如果你在基于类的组件中也有同样的需求,那么你需要将回调函数传递给setState。它会在状态更新时立即执行。
this.setState(stateupdate,callback)
一旦状态被更新,回调函数将以最新的状态执行。

const StackOverflow = () => {
const [globalVariable, setGlobalVariable] = useState();

useEffect(()=>{
    const test = 5
    console.log(test) // return 5
    setGlobalVariable(test)
    console.log(globalVariable)
},[]);

return (
    <Container maxWidth="lg" style={{paddingTop:"5%"}}>
        <div>
           <h1>StackOverflow - {globalVariable}</h1>
        </div>
    </Container>
)

}

yxyvkwin

yxyvkwin6#

这在官方文档§caveats中有很好的解释:
set函数只更新 next render的状态变量。如果在调用set函数后读取状态变量,则仍将获得调用前屏幕上的旧值。
特别是§故障排除:我已更新状态,但日志记录为我提供了旧值

piah890a

piah890a7#

出现错误的原因是,最初状态变量userData未定义。您试图在JSX代码中访问userData的name属性,但它尚未定义。因此,您需要添加一个条件,以在渲染JSX代码之前检查userData是否存在。您可以使用&&运算符来实现此目的。
您可以使用“{userData &&

{userData.name}

}”in your JSX in react

7gyucuyw

7gyucuyw8#

setGlobalVariable(test);需要一些时间,并且你试图在useEffect中执行它,它只运行一次,你应该使用setTimeOut函数花一些时间来显示状态,尝试这个并让我知道它是否工作。

const [globalVariable, setGlobalVariable] = useState();

  useEffect(() => {
    const test = 5;
    // console.log(test); // return 5
    setGlobalVariable(test);
    setTimeout(() => {
      console.log(globalVariable);
    }, 2000);
  }, []);

相关问题