setState 不会立即更新状态 [duplicate]

setState doesn't update the state immediately [duplicate]

提问人:Sydney Loteria 提问时间:12/22/2016 最后编辑:Mayank Kumar ChaudhariSydney Loteria 更新时间:2/2/2023 访问量:199593

问:

我想问一下为什么当我做一个活动时,我的状态没有改变。我刚才搜索过我需要在构造函数中绑定函数,但状态仍然没有更新。onClickonClick

这是我的代码:

import React from 'react';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import BoardAddModal from 'components/board/BoardAddModal.jsx';    
import style from 'styles/boarditem.css';

class BoardAdd extends React.Component {
    constructor(props) {
        super(props);    
        this.state = {
            boardAddModalShow: false
        };    
        this.openAddBoardModal = this.openAddBoardModal.bind(this);
    }

    openAddBoardModal() {
        this.setState({ boardAddModalShow: true }); // set boardAddModalShow to true

        /* After setting a new state it still returns a false value */
        console.log(this.state.boardAddModalShow);   
    }

    render() {    
        return (
            <Col lg={3}>
                <a href="javascript:;" 
                   className={style.boardItemAdd} 
                   onClick={this.openAddBoardModal}>
                    <div className={[style.boardItemContainer,
                                     style.boardItemGray].join(' ')}>
                        Create New Board
                    </div>
                </a>
            </Col>
        );
    }
}

export default BoardAdd
JavaScript 节点.js 反应JS ecmascript-6

评论

10赞 T.J. Crowder 8/19/2019
你在这个问题上接受的答案是没有意义的。 不返回 promise。如果它有效,它之所以有效,则只是因为在函数中引入了一个异步“tick”,并且恰好在该 tick 期间处理了状态更新。这不能保证。正如这个答案所说,你需要使用完成回调(如果你真的需要在状态更新后做一些事情,这是不寻常的;通常,你只想重新渲染,这是自动发生的)。setStateawait
3赞 Guy Incognito 5/28/2020
如果您不接受当前接受的答案或接受正确的答案,那就太好了,因为这可以用作许多其他问题的重复项。在顶部有一个错误的答案是误导性的。

答:

216赞 Shubham Khatri 12/22/2016 #1

您的状态需要一些时间来改变,并且由于在状态改变之前执行,因此您可以获得前一个值作为输出。所以你需要在函数的回调中写控制台console.log(this.state.boardAddModalShow)setState

openAddBoardModal() {
  this.setState({ boardAddModalShow: true }, function () {
    console.log(this.state.boardAddModalShow);
  });
}

setState是异步的。这意味着您不能在一行上调用它,并假设状态在下一行已更改。

根据 React 文档

setState()不会立即变异,但会创建一个 挂起状态转换。调用此项后访问 方法可能会返回现有值。没有 保证对 setState 的调用的同步操作,并且调用可能 进行批处理以提高性能。this.statethis.state

他们为什么要异步setState

这是因为会更改状态并导致重新渲染。这 可能是一项代价高昂的操作,使其同步可能会离开 浏览器无响应。setState

因此,调用是异步的,也是批处理的,以便更好地进行 UI 体验和性能。setState

评论

0赞 maudev 1/25/2019
到目前为止,这是一个好主意,但是如果我们不能使用console.log,因为您使用的是eslint规则怎么办?
2赞 Shubham Khatri 1/25/2019
@maudev,eslint 规则会阻止您拥有 console.logs,这样它们就不会最终进入生产环境,但上面的示例纯粹用于调试。和 console.log,并替换为考虑更新状态的操作
1赞 Ghasem 8/29/2019
如果回调没有像你说的那样工作怎么办?我的没有!
1赞 Shubham Khatri 4/20/2021
@VaibhavSidapara 查看这篇文章。如果有帮助,请告诉我
1赞 Vaibhav Sidapara 4/22/2021
@ShubhamKhatri谢谢,我昨天解决了。我使用了相同的useEffect钩子。
59赞 Mustkeem K 7/9/2018 #2

幸运的是,需要回调。这就是我们获得更新状态的地方。setState()

请看这个例子。

this.setState({ name: "myname" }, () => {                              
        //callback
        console.log(this.state.name) // myname
      });

因此,当回调触发时,this.state 是更新后的状态。
您可以在回调中获取数据。
mutated/updated

评论

1赞 Dende 12/21/2018
您还可以使用此回调来传递任何函数,例如initMap()
1赞 Ahmet Halilović 8/14/2020
好!终于解决了我的问题。多谢。
1赞 Gopal Mishra 10/13/2021
这会在 ReactJS 功能组件中发出警告
0赞 santu prajapati 2/21/2023
不依赖于状态,它会更新状态,您将通过变量获得更新值。让类别 = [...selectedCategory, value] setSelectedCategory(categories);setInputValue(categories),使用这段代码,您将获得更新值,因为您不依赖于状态。
16赞 Adnan 7/20/2018 #3

由于 setSatate 是一个异步函数,因此您需要像这样将状态作为回调进行控制台。

openAddBoardModal(){
    this.setState({ boardAddModalShow: true }, () => {
        console.log(this.state.boardAddModalShow)
    });
}
2赞 Ravin Gupta 7/29/2018 #4

如果要跟踪状态是否正在更新,那么执行相同操作的另一种方法是

_stateUpdated(){
  console.log(this.state. boardAddModalShow);
}

openAddBoardModal(){
  this.setState(
    {boardAddModalShow: true}, 
    this._stateUpdated.bind(this)
  );
}

这样,每次尝试更新调试状态时,都可以调用该方法“_stateUpdated”。

2赞 Spock 9/7/2018 #5

这个回调真的很乱。只需改用 async await:

async openAddBoardModal(){
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}

评论

61赞 T.J. Crowder 8/19/2019
这毫无意义。React 不返回 promise。setState
24赞 Mike Richards 10/4/2019
@T.J.克劳德是对的。 不返回 promise,因此不应等待。也就是说,我想我可以理解为什么这对某些人有用,因为 await 将 setState 的内部工作放在调用堆栈上,先于函数的其余部分,因此它首先被处理,因此看起来状态已经设置好了。如果 setState 具有或实现了任何新的异步调用,则此答案将失败。要正确实现此功能,可以使用以下命令:setStateawait new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
0赞 Ondřej Ševčík 9/5/2020
到底是怎么解决我的问题的?我们有工作代码,已经完成了更多的开发,但没有任何改变应用程序的那部分。setState 中的两个对象中只有一个被更新,使其异步返回功能恢复,现在两个对象都正确更新。我简直不知道到底出了什么问题。async this.setState({})
0赞 carmolim 10/2/2020
我找到了这个问题,但这个解决方案对我不起作用,我还没有解决我的问题,但我在这里找到了一个很好的阅读:ozmoroz.com/2018/11/why-my-setstate-doesn't-work
1赞 Emile Bergeron 3/11/2021
@OndřejŠevčík最有可能的是,竞争条件使它看起来像是有效的,但有一天它可能会再次中断。
8赞 Aman Seth 11/27/2018 #6

setState()并不总是立即更新组件。它可能会批处理或推迟更新,直到以后。这使得在调用后立即阅读 this.state 成为潜在的陷阱。请改用 use 或 callback (),保证在应用更新后触发其中任何一个。如果需要根据以前的状态设置状态,请阅读下面的 updater 参数。setState()componentDidUpdatesetStatesetState(updater, callback)

setState()将始终导致重新渲染,除非返回 false。如果正在使用可变对象,并且无法在 中实现条件渲染逻辑,则仅当新状态与先前状态不同时调用将避免不必要的重新渲染。shouldComponentUpdate()shouldComponentUpdate()setState()

第一个参数是带有签名的更新程序函数:

(state, props) => stateChange

state是对应用更改时组件状态的引用。它不应该被直接突变。相反,更改应该通过基于 state 和 props 的输入构建一个新对象来表示。例如,假设我们想通过 props.step 增加 state 中的值:

this.setState((state, props) => {
    return {counter: state.counter + props.step};
});
1赞 stuli 3/3/2019 #7

setState()是异步的。验证状态是否正在更新的最佳方法是在 而不是放在 之后。componentDidUpdate()console.log(this.state.boardAddModalShow)this.setState({ boardAddModalShow: true })

根据 React Docs

将 setState() 视为一个请求,而不是更新组件的即时命令。为了获得更好的感知性能,React 可能会延迟它,然后在一次传递中更新多个组件。React 不保证立即应用状态更改

-1赞 Atchutha rama reddy Karri 4/12/2019 #8
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })

评论

4赞 Emile Bergeron 5/23/2020
这正是最受好评的答案所解释的,但没有任何解释,而且晚了 3 年。请不要发布重复的答案,除非您有相关内容要添加。
1赞 khizer 5/18/2019 #9

根据 React Docs

React 不保证状态更改会立即应用。 这使得在调用 setState() 后立即读取 this.state 成为一种可能性,并且由于性质的原因,可能会返回值。 请改用 或 setState 操作成功后立即执行的回调。通常,我们建议改用此类逻辑。pitfallexistingasynccomponentDidUpdatesetStatecomponentDidUpdate()

例:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      counter: 1
    };
  }
  componentDidUpdate() {
    console.log("componentDidUpdate fired");
    console.log("STATE", this.state);
  }

  updateState = () => {
    this.setState(
      (state, props) => {
        return { counter: state.counter + 1 };
      });
  };
  render() {
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <button onClick={this.updateState}>Update State</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

-2赞 Dipanshu Sharma 8/28/2019 #10

是的,因为 setState 是一个异步函数。在编写 set state 后立即设置状态的最佳方法是使用 Object.assign,如下所示: 例如,您要将属性 isValid 设置为 true,请像这样操作


Object.assign(this.state, { isValid: true })


您可以在编写此行后立即访问更新的状态。

评论

1赞 Emile Bergeron 5/19/2020
不要这样做,它会改变当前状态,这是一种反模式。
-1赞 Pravin Ghorle #11

当我运行代码并在控制台检查我的输出时,它显示它未定义。 在我四处搜索并找到对我有用的东西之后。

componentDidUpdate(){}

我在 constructor() 之后的代码中添加了此方法。 查看 React Native 工作流的生命周期。

https://images.app.goo.gl/BVRAi4ea2P4LchqJ8

21赞 Luke Miles 7/22/2020 #12

对于任何尝试使用钩子执行此操作的人,您需要 .useEffect

function App() {
  const [x, setX] = useState(5)
  const [y, setY] = useState(15) 

  console.log("Element is rendered:", x, y)

  // setting y does not trigger the effect
  // the second argument is an array of dependencies
  useEffect(() => console.log("re-render because x changed:", x), [x])

  function handleXClick() {
    console.log("x before setting:", x)
    setX(10)
    console.log("x in *line* after setting:", x)
  }

  return <>
    <div> x is {x}. </div>
    <button onClick={handleXClick}> set x to 10</button>
    <div> y is {y}. </div>
    <button onClick={() => setY(20)}> set y to 20</button>
  </>
}

输出:

Element is rendered: 5 15
re-render because x changed: 5
(press x button)
x before setting: 5
x in *line* after setting: 5
Element is rendered: 10 15
re-render because x changed: 10
(press y button)
Element is rendered: 10 20

实时版

4赞 M Fuat 12/13/2020 #13

将 视为请求,而不是立即命令 更新组件。为了获得更好的感知性能,React 可以 延迟它,然后在一次传递中更新多个组件。反应 不保证立即应用状态更改。setState()

有关详细信息,请查看内容。

在您的情况下,您已发送更新状态的请求。React 需要时间来响应。如果尝试立即进入状态,则将获得旧值。console.log

2赞 Mayank Kumar Chaudhari 3/24/2021 #14

尽管有很多好的答案,但如果有人登陆此页面,寻找实现 UI 组件(如导航抽屉)的替代方案,这些组件应根据用户输入打开或关闭,则此答案将有所帮助。useState

虽然看起来很方便,但状态不会立即设置,因此,您的网站或应用程序看起来很滞后......如果你的页面足够大,react 将需要很长时间来计算在状态更改时应该更新哪些内容......useState

我的建议是,当您希望 UI 立即更改以响应用户操作时,请使用 refs 并直接操作 DOM。

在反应的情况下,将状态用于此目的确实是一个坏主意。

3赞 NevetsKuro 10/25/2021 #15

上述解决方案不适用于 useState 钩子。 可以使用以下代码

setState((prevState) => {
        console.log(boardAddModalShow)
        // call functions
        // fetch state using prevState and update
        return { ...prevState, boardAddModalShow: true }
    });

评论

0赞 velkoon 11/2/2022
这对我来说毫无意义。我无法将其翻译成我的应用程序。我想做的就是立即钩住变量。为什么这这么难?setMyVariable(false)