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
このコールバックは本当に面倒です。代わりにasync awaitを使用してください:
async openAddBoardModal(){
await this.setState({ boardAddModalShow: true });
console.log(this.state.boardAddModalShow);
}
状態が変化するのに時間がかかります。状態が変化する前にconsole.log(this.state.boardAddModalShow)
が実行されるため、出力として前の値を取得します。したがって、setState関数へのコールバックでコンソールを作成する必要があります
openAddBoardModal(){
this.setState({ boardAddModalShow: true }, function () {
console.log(this.state.boardAddModalShow);
});
}
setState
は非同期です。つまり、ある行でsetStateを呼び出して、次の行で状態が変化したと想定することはできません。
setState()
はすぐにthis.state
を変更しませんが、保留中の状態遷移を作成します。このメソッドを呼び出した後にthis.state
にアクセスすると、既存の値が返される可能性があります。 setStateの呼び出しの同期操作の保証はなく、パフォーマンス向上のために呼び出しをバッチ処理できます。
setStateを非同期にする理由
これは、setStateが状態を変更し、再レンダリングを引き起こすためです。これはコストのかかる操作になる可能性があり、同期化するとブラウザが応答しなくなる場合があります。
したがって、setState呼び出しは非同期であり、UIエクスペリエンスとパフォーマンスを向上させるためにバッチ処理されます。
幸いなことに setStateはコールバックを受け取ります。そして、ここで更新された状態を取得します。この例を考えてみましょう。
this.setState(
{ name: "Mustkeom" },
() => { //callback
console.log(this.state.name) // Mustkeom
}
);
そのため、コールバックが発生すると、this.stateは更新された状態になります。コールバックで変更/更新されたデータを取得できます。
SetSatateは非同期関数なので、このようなコールバックとして状態をコンソールする必要があります。
openAddBoardModal(){
this.setState({ boardAddModalShow: true }, () => {
console.log(this.state.boardAddModalShow)
});
}
setState()
は、必ずしもすぐにコンポーネントを更新するとは限りません。更新がバッチ処理されるか、後まで延期される場合があります。これにより、setState()
を呼び出した直後にthis.stateを読み取ることが潜在的な落とし穴になります。代わりに、componentDidUpdate
またはsetState
コールバック(setState(updater, callback)
)を使用します。いずれも、更新プログラムの適用後に起動することが保証されています。前の状態に基づいて状態を設定する必要がある場合は、以下のupdater引数についてお読みください。
setState()
は、shouldComponentUpdate()
がfalseを返さない限り、常に再レンダリングされます。可変オブジェクトが使用されており、shouldComponentUpdate()
で条件付きレンダリングロジックを実装できない場合、新しい状態が前の状態と異なる場合にのみsetState()
を呼び出すと、不要な再レンダリングが回避されます。
最初の引数は、署名付きの更新関数です。
(state, props) => stateChange
state
は、変更が適用される時点でのコンポーネントの状態への参照です。直接変異させないでください。代わりに、状態と小道具からの入力に基づいて新しいオブジェクトを構築することにより、変更を表す必要があります。たとえば、props.stepによって状態の値をインクリメントしたいとします。
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
はい。setStateは非同期関数であるためです。設定状態を記述した直後に状態を設定する最良の方法は、次のようにObject.assignを使用することです。たとえば、プロパティisValidをtrueに設定するには、次のようにします。
Object.assign(this.state、{isValid:true})
この行を書いた直後に更新された状態にアクセスできます。
状態が更新されているかどうかを追跡する場合、同じことを行う別の方法は
_stateUpdated(){
console.log(this.state. boardAddModalShow);
}
openAddBoardModal(){
this.setState(
{boardAddModalShow: true},
this._stateUpdated.bind(this)
);
}
これにより、デバッグのために状態を更新しようとするたびに、メソッド「_stateUpdated」を呼び出すことができます。
setStateは非同期関数であるため、次のように使用する必要がある場合があります。
this.setState({key:value},()=>{ callYourFunction(this.state.key) });
setState()
は非同期です。状態が更新されているかどうかを確認する最善の方法は、componentDidUpdate()
の後にconsole.log(this.state.boardAddModalShow)
を置かずにthis.setState({ boardAddModalShow: true })
を使用することです。
React Docs に従って
SetState()は、コンポーネントを更新するための即時コマンドではなく、リクエストと考えてください。パフォーマンスを向上させるために、Reactはそれを遅らせ、1回のパスでいくつかのコンポーネントを更新します。 Reactは、状態の変更がすぐに適用されることを保証しません
React Docs による
Reactは、状態の変更がすぐに適用されることを保証しません。これにより、setState()を呼び出した直後にthis.stateが潜在的な
pitfall
になり、existing
の性質によりasync
値を返す可能性があります。代わりに、setState操作が成功した直後に実行されるcomponentDidUpdate
または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);
this.setState({
isMonthFee: !this.state.isMonthFee,
}, () => {
console.log(this.state.isMonthFee);
})