setState không cập nhật trạng thái ngay lập tức


103

Tôi muốn hỏi tại sao trạng thái của tôi không thay đổi khi tôi thực hiện một sự kiện onclick. Tôi đã tìm kiếm một lúc trước rằng tôi cần liên kết hàm onclick trong hàm tạo nhưng trạng thái vẫn không cập nhật. Đây là mã của tôi:

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
Câu trả lời bạn đã chấp nhận cho câu hỏi này không có ý nghĩa gì. setStatekhông trả lại một lời hứa. Nếu nó hoạt động, nó chỉ hoạt động vì awaitgiới thiệu một "đánh dấu" không đồng bộ vào hàm và đã xảy ra rằng bản cập nhật trạng thái đã được xử lý trong lần đánh dấu đó. Nó không được đảm bảo. Như câu trả lời này cho biết, bạn cần sử dụng lệnh gọi lại hoàn thành (nếu bạn thực sự cần làm gì đó sau khi trạng thái được cập nhật, điều này không bình thường; thông thường, bạn chỉ muốn kết xuất lại, nó sẽ tự động mở).
TJ Crowder

Sẽ rất tốt nếu bạn không chấp nhận câu trả lời hiện đang được chấp nhận hoặc chấp nhận một câu đúng, vì sau đó câu trả lời này có thể được sử dụng làm bản sao cho nhiều câu hỏi khác. Có một câu trả lời không chính xác ở trên cùng là gây hiểu lầm.
Guy Incognito

Câu trả lời:


12

Cuộc gọi lại này thực sự rất lộn xộn. Chỉ cần sử dụng async await để thay thế:

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

24
Điều đó không có ý nghĩa. React's setStatekhông trả lại một lời hứa.
TJ Crowder

16
@TJCrowder đã đúng. setStatekhông trả lại một lời hứa, vì vậy nó KHÔNG nên được chờ đợi. Điều đó nói rằng, tôi nghĩ rằng tôi có thể hiểu tại sao điều này lại hiệu quả với một số người, bởi vì chờ đợi đặt các hoạt động bên trong của setState trên ngăn xếp lệnh gọi trước phần còn lại của hàm, vì vậy nó được xử lý đầu tiên và do đó, có vẻ như trạng thái đã được bộ. Nếu setstate có hoặc dụng cụ bất kỳ cuộc gọi không đồng bộ mới, câu trả lời này sẽ fail.To thực hiện chức năng này đúng cách, bạn có thể sử dụng:await new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
Mike Richards

Làm thế quái nào async this.setState({})giải quyết được vấn đề của tôi? Chúng tôi đã có mã hoạt động, một số phát triển khác đã được thực hiện nhưng không có gì thay đổi phần đó của ứng dụng. Chỉ có một trong hai đối tượng trong setState được cập nhật, làm cho nó không đồng bộ hóa chức năng trở lại và bây giờ cả hai đối tượng đang cập nhật chính xác. Tôi hoang mang không biết chuyện quái gì đã xảy ra.
Ondřej Ševčík

145

Trạng thái của bạn cần một khoảng thời gian để thay đổi và vì console.log(this.state.boardAddModalShow)thực thi trước khi trạng thái thay đổi, bạn nhận được giá trị trước đó làm đầu ra. Vì vậy, bạn cần viết bảng điều khiển trong lệnh gọi lại setStatehàm

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

setStatelà không đồng bộ. Nó có nghĩa là bạn không thể gọi nó trên một dòng và cho rằng trạng thái đã thay đổi ở dòng tiếp theo.

Theo React docs

setState()không đột biến ngay lập tức this.statemà tạo ra sự chuyển trạng thái chờ xử lý. Việc truy cập this.statesau khi gọi phương thức này có thể trả về giá trị hiện có. Không có gì đảm bảo hoạt động đồng bộ của các cuộc gọi đến setState và các cuộc gọi có thể được thực hiện theo đợt để tăng hiệu suất.

Tại sao họ có thể làm cho setStateasync

Điều này là do setStatethay đổi trạng thái và gây ra kết xuất. Đây có thể là một hoạt động tốn kém và việc làm cho nó đồng bộ có thể khiến trình duyệt không phản hồi.

Do đó, các setStatecuộc gọi không đồng bộ cũng như theo đợt để có trải nghiệm và hiệu suất giao diện người dùng tốt hơn.


Cho đến bây giờ đó là một ý tưởng tuyệt vời, NHƯNG điều gì sẽ xảy ra nếu chúng ta không thể sử dụng console.log vì bạn đang sử dụng các quy tắc eslint?
maudev

2
@maudev, các quy tắc eslint ngăn bạn có console.logs để chúng không kết thúc trong quá trình sản xuất, nhưng ví dụ trên hoàn toàn là để gỡ lỗi. và console.log và được thay thế bằng một hành động mà sẽ đưa vào tài khoản các trạng thái được cập nhật
Shubham Khatri

1
Điều gì sẽ xảy ra nếu cuộc gọi lại không hoạt động như bạn đã nói? của tôi không!
Alex Jolig

33

May mắn thay setState() có một cuộc gọi lại. Và đây là nơi chúng tôi nhận được trạng thái cập nhật.

Hãy xem xét ví dụ này.

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

Vì vậy, khi gọi lại kích hoạt, this.state là trạng thái được cập nhật.
Bạn có thể lấy mutated/updateddữ liệu trong cuộc gọi lại.


1
Bạn cũng có thể sử dụng lệnh gọi lại này để chuyển bất kỳ hàm nào initMap(), chẳng hạn như
Dende 21/1218

Tốt thôi! Cuối cùng đã giải quyết được vấn đề của tôi. Cảm ơn rất nhiều.
Ahmet Halilović

11

Vì setSatate là một hàm không đồng bộ nên bạn cần điều khiển trạng thái dưới dạng một lệnh gọi lại như thế này.

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

6

setState()không phải lúc nào cũng cập nhật thành phần ngay lập tức. Nó có thể hàng loạt hoặc trì hoãn cập nhật cho đến sau này. Điều này làm cho việc đọc this.state ngay sau khi gọi setState()một cạm bẫy tiềm ẩn. Thay vào đó, hãy sử dụng componentDidUpdatehoặc setStatecallback ( setState(updater, callback)), một trong hai được đảm bảo sẽ kích hoạt sau khi áp dụng bản cập nhật. Nếu bạn cần đặt trạng thái dựa trên trạng thái trước đó, hãy đọc về đối số trình cập nhật bên dưới.

setState()sẽ luôn dẫn đến kết xuất lại trừ khi shouldComponentUpdate()trả về false. Nếu các đối tượng có thể thay đổi đang được sử dụng và không thể thực hiện logic kết xuất có điều kiện shouldComponentUpdate(), setState()chỉ gọi khi trạng thái mới khác với trạng thái trước đó sẽ tránh được các kết xuất không cần thiết.

Đối số đầu tiên là một hàm cập nhật với chữ ký:

(state, props) => stateChange

statelà một tham chiếu đến trạng thái thành phần tại thời điểm thay đổi đang được áp dụng. Nó không nên bị đột biến trực tiếp. Thay vào đó, các thay đổi nên được thể hiện bằng cách xây dựng một đối tượng mới dựa trên đầu vào từ trạng thái và đạo cụ. Ví dụ: giả sử chúng ta muốn tăng một giá trị ở trạng thái bằng props.step:

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

2

Nếu bạn muốn theo dõi trạng thái có cập nhật hay không thì một cách khác để làm điều tương tự là

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

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

Bằng cách này, bạn có thể gọi phương thức "_stateUpdated" mỗi khi bạn cố gắng cập nhật trạng thái để gỡ lỗi.


1

setState()là không đồng bộ. Cách tốt nhất để xác minh xem trạng thái có đang cập nhật hay không sẽ là componentDidUpdate()và không phải đặt lại console.log(this.state.boardAddModalShow)sau this.setState({ boardAddModalShow: true }).

theo React Docs

Hãy coi setState () như một yêu cầu hơn là một lệnh tức thì để cập nhật thành phần. Để có hiệu suất tốt hơn, React có thể trì hoãn nó, và sau đó cập nhật một số thành phần trong một lần chuyển. React không đảm bảo rằng các thay đổi trạng thái được áp dụng ngay lập tức


1

Theo React Docs

React không đảm bảo rằng các thay đổi trạng thái được áp dụng ngay lập tức. Điều này làm cho việc đọc this.state ngay sau khi gọi setState () là một tiềm năng pitfallvà có thể trả về existinggiá trị do asynctự nhiên. Thay vào đó, hãy sử dụng componentDidUpdatehoặc một lệnh setStategọi lại được thực thi ngay sau khi thao tác setState thành công. Nói chung, chúng tôi khuyên bạn nên sử dụng componentDidUpdate()cho logic như vậy thay thế.

Thí dụ:

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

Đối với bất kỳ ai cố gắng làm điều này với móc, bạn cần 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>
  </>
}

Đầu ra:

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

khi tôi đang chạy mã và kiểm tra đầu ra của tôi tại bảng điều khiển, nó cho thấy rằng nó là không xác định. Sau khi tôi tìm kiếm xung quanh và tìm thấy một cái gì đó phù hợp với tôi.

componentDidUpdate(){}

Tôi đã thêm phương thức này vào mã của mình sau hàm tạo (). kiểm tra vòng đời của quy trình làm việc gốc phản ứng.

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


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

2
Đây chính xác là những gì câu trả lời được ủng hộ nhiều nhất giải thích, nhưng không có bất kỳ lời giải thích nào và đã trễ 3 năm. Vui lòng không đăng các câu trả lời trùng lặp trừ khi bạn có điều gì đó liên quan để thêm vào nó.
Emile Bergeron

-2

Có vì setState là một hàm không đồng bộ. Cách tốt nhất để đặt trạng thái ngay sau khi bạn viết trạng thái thiết lập là sử dụng Object.assign như sau: Ví dụ: bạn muốn đặt thuộc tính isValid thành true, hãy làm như thế này


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


Bạn có thể truy cập trạng thái cập nhật chỉ sau khi viết dòng này.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.