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それ機能した場合、機能に1つの非同期「ティック」が導入されたためにのみ機能し、そのティック中に状態の更新が処理されることがありました。保証はできません。この答えは言う(あなたが本当に状態が特殊なケースですが、更新された後に何かをする必要がある場合は、通常、あなただけ自動的にhapensれ、再レンダリングする)、あなたは完了コールバックを使用する必要があります。
TJクラウダー

現在受け入れられている回答の受け入れを解除するか、正しい回答を受け入れれば、これは他の多くの質問の複製として使用できるため、良いでしょう。上部に不正解があると誤解を招きます。
Guy Incognito

回答:


12

このコールバックは本当に厄介です。代わりにasync awaitを使用してください:

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

24
それは意味がありません。React setStateは約束を返しません。
TJクラウダー

16
@TJCrowderは正しいです。setState約束を返さないので、待ってはいけません。とは言っても、これが一部の人にとってうまくいく理由は理解できると思います。awaitが関数の残りの部分よりも前にコールスタックのsetStateの内部処理を行うため、最初に処理され、状態がセットする。:SETSTATEが持っていたか、実装するすべての新しい非同期呼び出しは、この答えはfail.Toが適切にこの機能を実装している場合は、この使用することができますawait new Promise(resolve => this.setState({ boardAddModalShow: true }, () => resolve()))
マイク・リチャーズ

地獄はasync this.setState({})私の問題をどのように解決しましたか?作業コードがありましたが、さらに開発が行われましたが、アプリのその部分は何も変更されていません。setStateの2つのオブジェクトのうち1つだけが更新されていたため、非同期で機能が返され、両方のオブジェクトが正しく更新されています。私は一体何が悪いのかわからなかった。
オンドレイŠ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

1
あなたが言ったようにコールバックが機能しない場合はどうなりますか?私はしません!
Alex Jolig

33

幸いにも setState()コールバックを受け取ります。そして、ここで状態が更新されます。

この例を考えてみましょう。

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

したがって、コールバックが発生すると、this.stateが更新された状態になります。コールバックでデータを
取得できmutated/updatedます。


1
このコールバックを使用して、任意の関数を渡すこともできinitMap()ます。たとえば、
Dende

行くぞ!最後に私の問題を解決しました。どうもありがとう。
アフメットハリロビッチ

11

setSatateは非同期関数なので、このようなコールバックとして状態をコンソールする必要があります。

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

6

setState()常にコンポーネントがすぐに更新されるわけではありません。更新をバッチ処理するか、後で延期する場合があります。これによりsetState()、潜在的な落とし穴を呼び出した直後にthis.stateを読み取ることができます。代わりに、componentDidUpdateまたはsetStateコールバック(setState(updater, callback))を使用してください。どちらも、更新が適用された後に起動することが保証されています。以前の状態に基づいて状態を設定する必要がある場合は、以下のupdater引数についてお読みください。

setState()shouldComponentUpdate()falseが返されない限り、常に再レンダリングが行われます。可変オブジェクトが使用されており、条件付きレンダリングロジックをで実装できない場合はshouldComponentUpdate()setState()場合、新しい状態が前の状態と異なる場合にのみことで、不要な再レンダリングを回避できます。

最初の引数は、シグネチャを持つ更新関数です。

(state, props) => stateChange

state変更が適用されているときのコンポーネントの状態への参照です。直接変更しないでください。代わりに、状態と小道具からの入力に基づいて新しいオブジェクトを作成することで、変更を表す必要があります。たとえば、statesの値を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はそれを遅延させ、1つのパスで複数のコンポーネントを更新する場合があります。Reactは、状態の変更がすぐに適用されることを保証しません


1

React Docsによると

Reactは、状態の変更がすぐに適用されることを保証しません。これにより、setState()を呼び出した直後にthis.stateを読み取るpitfallことが可能になり、自然のexistingために値を返す可能性がasyncあります。代わりに、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);

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(){}

私はこのメソッドを私のコンストラクタのコンストラクタの後に追加しました。反応ネイティブワークフローのライフサイクルを確認してください。

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



-2

はい。setStateは非同期関数であるためです。set stateを書き込んだ直後に状態を設定する最良の方法は、次のようにObject.assignを使用することです。たとえば、プロパティisValidをtrueに設定するには、次のようにします。


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


この行を書き込んだ直後に、更新された状態にアクセスできます。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.