簡體   English   中英

setState 不會立即更新狀態

[英]setState doesn't update the state immediately

我想問一下為什么當我執行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 }); // 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

您的狀態需要一些時間來改變,並且由於console.log(this.state.boardAddModalShow)在狀態改變之前執行,您將獲得先前的值作為輸出。 所以需要在setState函數的回調中寫console

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

setState是異步的。 這意味着您不能在一行調用它並假設狀態在下一行發生了變化。

根據React 文檔

setState()不會立即改變this.state而是創建一個掛起的狀態轉換。 調用此方法后訪問this.state可能會返回現有值。 無法保證對 setState 調用的同步操作,並且可能會批量調用以提高性能。

為什么他們會讓setState異步

這是因為setState會改變狀態並導致重新渲染。 這可能是一項昂貴的操作,並且使其同步可能會使瀏覽器無響應。

因此setState調用是異步的,並且是批處理的,以獲得更好的 UI 體驗和性能。

幸運的是setState()需要一個回調。 這就是我們獲得更新狀態的地方。

考慮這個例子。

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

所以當回調觸發時, this.state 是更新的狀態。
您可以在回調中獲取mutated/updated數據。

由於 setSatate 是一個異步函數,因此您需要像這樣將狀態作為回調進行控制台。

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

setState()並不總是立即更新組件。 它可能會批量更新或推遲更新。 這使得在調用setState()之后立即讀取 this.state 成為一個潛在的陷阱。 相反,使用componentDidUpdatesetState回調( setState(updater, callback) ),這兩者都保證在應用更新后觸發。 如果您需要根據之前的狀態設置狀態,請閱讀下面的更新程序參數。

setState()將始終導致重新渲染,除非shouldComponentUpdate()返回 false。 如果正在使用可變對象並且在shouldComponentUpdate()無法實現條件渲染邏輯,則僅在新狀態與先前狀態不同時調用setState()將避免不必要的重新渲染。

第一個參數是帶有簽名的更新程序函數:

(state, props) => stateChange

state是對應用更改時組件狀態的引用。 它不應該被直接變異。 相反,應該通過基於 state 和 props 的輸入構建一個新對象來表示更改。 例如,假設我們想通過 props.step 增加 state 中的一個值:

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

對於嘗試使用鈎子執行此操作的任何人,您都需要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

這個回調真的很亂。 只需使用 async await 代替:

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

如果您想跟蹤狀態是否正在更新,那么另一種做同樣事情的方法是

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

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

通過這種方式,您可以在每次嘗試更新狀態以進行調試時調用方法“_stateUpdated”。

setState()視為更新組件的請求而不是立即命令。 為了獲得更好的感知性能,React 可能會延遲它,然后一次更新多個組件。 React 不保證狀態更改會立即應用。

檢查這個以獲取更多信息。

在您的情況下,您已發送更新狀態的請求 React 需要時間來響應。 如果您嘗試立即console.log狀態,您將獲得舊值。

setState()是異步的。 驗證狀態是否正在更新的最佳方法是在componentDidUpdate()而不是在this.setState({ boardAddModalShow: true })之后放置console.log(this.state.boardAddModalShow) this.setState({ boardAddModalShow: true })

根據React Docs

將 setState() 視為更新組件的請求而不是立即命令。 為了獲得更好的感知性能,React 可能會延遲它,然后一次更新多個組件。 React 不保證立即應用狀態更改

根據React 文檔

React 不保證狀態更改會立即應用。 這使得在調用 setState() 后立即讀取 this.state 成為一個潛在的pitfall並且由於async性質可能返回existing值。 相反,使用componentDidUpdate或在 setState 操作成功后立即執行的setState回調。通常我們建議使用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);

setState是一個異步函數,因此您可能需要將其用作,

this.setState({key:value},()=>{ callYourFunction(this.state.key) });
openAddBoardModal=async()=>{
    await this.setState({ boardAddModalShow: true });
    console.log(this.state.boardAddModalShow);
}

盡管有很多很好的答案,但如果有人在此頁面上搜索useState替代方法來實現 UI 組件,例如應根據用戶輸入打開或關閉的導航抽屜,則此答案會有所幫助。

雖然useState看起來很方便,但狀態不是立即設置的,因此,您的網站或應用程序看起來很慢……如果您的頁面足夠大,react 將需要很長時間來計算狀態更改時應更新的所有內容。 ..

我的建議是當您希望 UI 響應用戶操作而立即更改時,使用 refs 並直接操作 DOM。

在反應的情況下,為此目的使用狀態確實是一個壞主意。

上述解決方案不適用於 useState 鈎子。 可以使用下面的代碼

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

當我運行代碼並在控制台檢查我的輸出時,它顯示它是未定義的。 在我四處搜索並找到對我有用的東西之后。

componentDidUpdate(){}

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

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

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

是的,因為 setState 是一個異步函數。 在您編寫 set state 后立即設置 state 的最佳方法是使用 Object.assign 像這樣:例如,您想將一個屬性 isValid 設置為 true,請這樣做


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


您可以在編寫此行后立即訪問更新狀態。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM