useState() 在 React 中设置后未定义

Adr*_*urg 9 reactjs use-state

我有一个问题,我不明白为什么钩子返回 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;
Run Code Online (Sandbox Code Playgroud)

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

Ren*_*tto 10

正如其他成员所提到的,setState 在基于队列的系统中异步运行。这意味着如果要执行状态更新,则此更新将堆积在队列上。但是,它不返回 Promise,因此您不能使用 a.then或 a wait。因此,我们需要意识到在 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

这基本上与您遇到的问题相同。由于 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

这基本上回答了你的问题。

但是,在其他情况下,状态更新可能不会按预期运行。了解这些场景以避免可能出现的错误非常重要。例如,假设我们连续多次更新给定状态,例如:

// 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

这里,console.log仍然返回 0,原因与问题 #1 中描述的相同。但是,如果您在应用程序中的某处使用count状态,您将看到事件后的最终值为onClick1,而不是 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

通过将函数传递给 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>
  )
}
Run Code Online (Sandbox Code Playgroud)

onCountChange请注意,在这个问题中,我们有一个依赖于外部变量 ( )的函数count。通过外部我的意思是这个变量没有在 的范围内声明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>
  )
}
Run Code Online (Sandbox Code Playgroud)

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


Fed*_*kun 4

两件事情:

  • useState有一个初始值,默认为未定义。
const [globalVariable, setGlobalVariable] = useState(123); // initial value set to 123
Run Code Online (Sandbox Code Playgroud)
  • setGlobalVariable不是同步操作。改变它不会globalVariable立即变异。范围中的值将保持不变,直到下一个渲染阶段。