【问题标题】:state neither updated from useState nor useEffect inside useState [duplicate]状态既没有从 useState 更新,也没有在 useState 内更新 useEffect [重复]
【发布时间】:2021-01-27 09:38:18
【问题描述】:

我遇到了与this question几乎相同的问题

代码有点太多了,所以我做了一个精简版的问题。 (如果我做错了,请原谅我)

基本上,我有一个主组件和一个子组件

主要组件

import React {useState} from 'react'
import SubComponent from './subcomponent';

const Main = (props) => {
  const [state, setState] = useState(null);
  const updateStateFunction = (data) => {
    const newState = data
    console.log(state)
    setState(newState)
    console.log(state)
  }

  return (
    <div>
      <SubComponent
        updateStateFunction = {updateStateFunction}
      />
    </div>
  )
}


export default Main;

子组件

import React {useState} from 'react'
const SubComponent = ({updateStateFunction}) => {
  return (
    <div>
      <button
        onClick={() => updateStateFunction("Something new")}
      >       
      </button>
    </div>
  )
}
export default SubComponent;

两个控制台日志都给出null。

我的解决方案尝试:

  1. 由于大多数堆栈溢出答案表明使用钩子的状态更新是异步的,我尝试使用 setTimeout

  2. 我以为我们可以使用 async-await,但这是错误的方法

  3. 我尝试更新useEffect 中的状态,但关键是没有任何东西被重新渲染。这是因为正在更新的变量绝不是输出的一部分,而是对辅助变量进行排序以进行进一步的操作。 我这样做的方式是使用the solution in the above refereced question:

const Main = (props) => {

  /*Debugging*/
  let newState = null
  useEffect(()=>{
    console.log("useEffect called")
    setState(newState)
  }, [newState])
  /*Debugging*/

  const [state, setState] = useState(null);
  const updateStateFunction = (data) => {
    newState = data
    console.log(state)
    setState(newState)
    console.log(state)
  }

  return (
    <div>
      <SubComponent
        updateStateFunction = {updateStateFunction}
      />
    </div>
  )
}

我虽然因为 useEffect 钩子甚至没有被执行,所以我没有尝试the solution中的其他两种方法

我是否引用了错误类型的问题,或者这是反应中的常见行为?

很高兴在需要时提供更多信息


编辑:
  1. 我添加了console.log(),因为我有操作,然后是使用状态变量值的状态更改。

  2. 使用 React 开发工具,我看到状态正在更新,而且几乎是即时更新的。问题是按钮按下会导致实际代码中的对话框弹出,其组件将该状态用于其他逻辑,因此我得到一个错误,即该状态仍然为空

【问题讨论】:

  • setState() 在 react 中执行是异步的,不保证会在下一行更新,慎用
  • 这能回答你的问题吗? useState set method not reflecting change immediately @AshishKamble 实际上可以保证不会在与入队状态更新相同的渲染周期内被调用的任何函数中的任何行更新。
  • 这是同一个问题。你的状态为空,第一次记录,你排队更新状态,然后再次记录当前状态,仍然为空。它回答了您的问题,因为反应状态更新是异步的,并且会在 渲染周期之间进行更新,因此您可以从不 记录刚刚入队的状态。这个问题几乎每天都会被问到。只需将状态更新排队并将新状态记录在依赖于该状态的 useEffect 中。
  • 对,useState 更新函数不会返回 Promise,所以你不能等待它,setTimeout 回调只会包含当前状态。
  • @AshishKamble 绝对保证下一行不会反映更新的状态。 state 是来自闭包的 const 引用,无论 setState() 执行是否同步。

标签: javascript reactjs react-hooks react-functional-component


【解决方案1】:

我不确定let newState = null 与引用问题中的任何答案有什么关系,所以要清楚,这是直接应用接受的答案的方式:

const Main = (props) => {
  const [state, setState] = useState(null);
  const updateStateFunction = (data) => { setState(data) }

  useEffect(() => { console.log(state) }, [state])

  return <SubComponent updateStateFunction = {updateStateFunction} />
}

但是,如果状态不用于屏幕上呈现的任何内容,则更改状态毫无意义 - 如果 Reacts 没有检测到返回值的任何更改,它可能不会将任何更改提交给 DOM 作为优化,所以在这种情况下它可能不会运行任何useEffects。

我建议使用 React Dev Tools 来检查组件的当前状态。

另外,console.log 基本上是唯一不需要在useEffect 中的副作用。如果直接在渲染函数中,它将在渲染函数被调用时执行,并且不依赖于 React 提交对 DOM 的更改...

请注意,我对引用问题的回答中的第一个建议不会将 console.log 包装到 useEffect 中 - 再次明确,这是直接应用该建议的方式:

const Main = (props) => {
  const [state, setState] = useState(null);
  const updateStateFunction = (data) => { setState(data) }

  console.log(state)

  return <SubComponent updateStateFunction = {updateStateFunction} />
}

【讨论】:

  • 所以如果useEffect 不是解决方案,那么我应该声明某种全局变量吗?或使用上下文?或者是其他东西? (PS 我刚刚使用 React 开发工具检查了状态正在更新,而且几乎是立即更新。问题是按钮按下会导致对话框弹出,其组件将状态用于其他逻辑,因此我收到一个错误,即状态仍然为空)
  • @Geek 您必须为您遇到的实际问题创建stackoverflow.com/help/minimal-reproducible-example - 例如创建一个代码框,其中按钮按下会导致一个对话框弹出窗口,其内容将状态用于其他逻辑并且它在那里为空......但一般&lt;SubComponent state={state} ...是如何将状态从父母传递给孩子的推荐方式
【解决方案2】:

状态的设置在react中是异步的。 异步函数将作为其他指令并行执行(嗯,有点)。而不是在设置状态之后console.logging,您可以在返回函数之前通过console.log状态来了解新的输出。

您的第一个实施没有任何问题。此处无需使用useEffect

import React {useState} from 'react'
import SubComponent from './subcomponent';

const Main = (props) => {
  const [state, setState] = useState(null);
  const updateStateFunction = (data) => {
    setState(data)
  }
  
  // try console logging here
  console.log(state)
  return (
    <div>
      <SubComponent
        updateStateFunction = {updateStateFunction}
      />
    </div>
  )
}


export default Main;

这样想,无论何时设置状态,包含该状态的函数都会刷新并重新运行。您可以在函数中的任何位置使用 console.log 来获取新状态。

【讨论】:

  • 控制台记录它的原因是因为我有其他操作,然后是依赖于该变量状态的状态更改。
  • 什么样的操作?
【解决方案3】:

使用 react devtools 调试,即使 console.log() 显示 null 你在 devtools 中看到状态变化。原因是状态更新是异步的。

如果您仍想使用 console.log() 调试您的 React 应用程序,请在 return 语句之前调用它,甚至在 jsx 中直接使用花括号调用它。

【讨论】:

  • 控制台记录它的原因是因为我有其他操作,然后是依赖于该变量状态的状态更改。
  • 你可以使用useEffect,它在状态改变后立即执行。
猜你喜欢
  • 2021-10-24
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
  • 2021-03-03
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
相关资源
最近更新 更多