setState不会立即更新状态


103

我想问一下为什么在执行onclick事件时状态没有变化。我已经搜索了一段时间,我需要在构造函数中绑定onclick函数,但状态仍未更新。这是我的代码:

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 });
// After setting a new state it still return 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

5
您对此问题接受的答案没有任何意义。setState没有兑现承诺。如果它起作用了,那只能是因为await在函数中引入了一个异步“ tick”,并且碰巧在该tick期间处理了状态更新。不能保证。就像这个答案所说的,您需要使用完成回调(如果您确实需要在状态更新后做一些事情,这是不寻常的;通常,您只想重新渲染,这会自动发生)。
TJ Crowder

如果您不接受当前接受的答案或接受正确的答案,那将是很好的选择,因为这样可以将其用作其他许多问题的副本。顶部的答案有误会误导您。
Guy Incognito

Answers:


12

这个回调真的很混乱。只需使用async await代替:

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

24
这是没有意义的。React setState不会返回承诺。
TJ Crowder

16
@TJCrowder是正确的。setState不会返回承诺,因此不应等待。就是说,我想我可以理解为什么这对某些人有效,因为await将setState的内部工作放在调用堆栈上,而不是该函数的其余部分,因此它首先被处理,因此好像状态已经被处理了。组。如果setState拥有或实现了任何新的异步调用,则此答案将失败。要正确实现此功能,可以使用:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards,

到底怎么async this.setState({})解决了我的问题?我们有可用的代码,已经完成了一些开发工作,但没有更改应用程序的那部分。setState中只有两个对象之一正在更新,使其异步返回功能,现在两个对象都已正确更新。我不知道到底是怎么了。
的OndrejŠevčík

145

您的状态需要一些时间才能变异,由于console.log(this.state.boardAddModalShow)状态在变异之前就已执行,因此您会获得先前的值作为输出。因此,您需要在setState函数的回调中编写控制台

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

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

根据React docs

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

他们为什么要setState异步

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

因此,这些setState调用是异步的也可以是批处理的,以获得更好的UI体验和性能。


到目前为止,这是一个好主意,但是如果您因为使用eslint规则而不能使用console.log,该怎么办?
maudev

2
@ maudev,eslint规则会阻止您拥有console.logs,这样它们就不会最终投入生产,但以上示例仅用于调试。和console.log并被考虑到更新状态的动作所取代
Shubham Khatri 19'Jan

1
如果回调无法如您所言怎么办?我没有!
Alex Jolig

33

幸运的是 setState()进行了回调。这是我们获取更新状态的地方。

考虑这个例子。

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

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


1
您还可以使用此回调函数传递任何函数,initMap()例如
Dende

要走的路!终于解决了我的问题。非常感谢。
AhmetHalilović

11

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

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

6

setState()并不总是立即更新组件。它可能会批量更新或将更新推迟到以后。这使得在调用setState()潜在陷阱之后立即阅读this.state 。而是使用componentDidUpdatesetState回调(setState(updater, callback)),保证在应用更新后都会触发两者。如果您需要基于先前的状态来设置状态,请阅读以下有关updater参数的信息。

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

第一个参数是带有签名的updater函数:

(state, props) => stateChange

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

this.setState((state, props) => {
    return {counter: state.counter + props.step};
});

2

如果您想跟踪状态是否正在更新,那么做同一件事的另一种方法是

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

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

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


1

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

根据React Docs

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


1

根据React Docs

React不能保证状态更改会立即应用。这使得在调用setState()之后立即读取this.state成为可能,pitfall并且existing由于asyncnature ,可能会返回该值。而是使用setState操作成功后立即执行的componentDidUpdatesetState回调。通常,我们建议改用componentDidUpdate()此类逻辑。

例:

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);

0

对于尝试使用挂钩进行此操作的任何人,您都需要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

-1

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

componentDidUpdate(){}

我在构造函数()之后的代码中添加了此方法。查看react native工作流程的生命周期。

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


-2
 this.setState({
    isMonthFee: !this.state.isMonthFee,
  }, () => {
    console.log(this.state.isMonthFee);
  })

2
这正是最受支持的答案所解释的,但没有任何解释,并且迟了三年。除非您有其他要添加的内容,否则
Emile Bergeron

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.