setState は同期ですか、それとも非同期ですか?カスタム合成イベントと React フック関数で状態を非同期的に更新するカスタムクリックイベントのsetStateを例に挙げます React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } ハンドルクリック = () => { this.setState({ カウント: this.state.count + 1 }); this.setState({ カウント: this.state.count + 1 }); this.setState({ カウント: this.state.count + 1 }); console.log(この状態の数); } 与える() { 戻る ( <div style={{ 幅: '100px', 高さ: '100px', 背景色: "黄色" }}> {この州数} </div> ) } } デフォルトのテストをエクスポートします。 一度クリックすると、this.state.count の最終的な印刷結果は 1 になり、ページには 2 が表示されます。この現象から、3 つの setState のうち最後の setState のみが有効であり、最初の 2 つの setState は効果がないことがわかります。なぜなら、最初の setState を +3 に変更すると、count の印刷結果は 1 になり、表示結果は 2 になり、変化がないからです。また、カウント結果を取得するための同期はありません。 この時点で、setState の 2 番目のパラメータを通じて更新された状態を取得するようにコードを調整できます。 React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } ハンドルクリック = () => { this.setState({ カウント: this.state.count + 3 }, () => { console.log('1', this.state.count) }); this.setState({ カウント: this.state.count + 1 }, () => { console.log('2', this.state.count); }); this.setState({ カウント: this.state.count + 1 }, () => { console.log('3', this.state.count); }); console.log(この状態の数); } 与える() { 戻る ( <div style={{ 幅: '100px', 高さ: '100px', 背景色: "黄色" }}> {この州数} </div> ) } } デフォルトのテストをエクスポートします。 このとき、一度クリックすると、3 つの setState コールバック関数の印刷結果は次のようになります。
まず、最後の行は 1 を直接出力します。そして、setState のコールバックでは、印刷される結果はすべて最新の更新 2 になります。最初の 2 回の setState 呼び出しは有効になりませんでしたが、2 番目のパラメーターには 2 が引き続き出力されます。 このとき、setState の第一パラメータを関数に置き換え、関数の第一パラメータを通じて更新前の状態を取得できるようになります。 React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } ハンドルクリック = () => { this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }); console.log(この状態の数); } 与える() { 戻る ( <div style={{ 幅: '100px', 高さ: '100px', 背景色: "黄色" }}> {この州数} </div> ) } } デフォルトのテストをエクスポートします。 このとき、印刷結果は 1 ですが、ページに表示されるカウントは 4 です。 setState がパラメータを渡して状態を更新する場合、複数の setState 更新は最後の更新だけでなく、複数の更新に有効になることがわかります。 次に、2 番目の関数でどれだけの count が印刷されるかを見てみましょう。 React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } ハンドルクリック = () => { this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('1', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('2', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('3', this.state.count); }); console.log(この状態の数); } 与える() { 戻る ( <div style={{ 幅: '100px', 高さ: '100px', 背景色: "黄色" }}> {この州数} </div> ) } } デフォルトのテストをエクスポートします。 このとき、一度クリックすると、3つのsetStateコールバック関数での印刷結果は以下のようになります。ご想像のとおり、ページ表示結果も4です。
上記のコードをcomponentDidMountに入力すると、出力結果は上記と同じになります。 なぜなら、カスタム合成イベントとフック関数では、状態の更新が非同期であることがわかるからです。 ネイティブイベントとsetTimeoutで状態を同期的に更新するsetTimeoutのsetStateを例に挙げる React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } コンポーネントマウント() { タイムアウトを設定する(() => { this.setState({ カウント: this.state.count + 1 }, () => { console.log('1:', this.state.count); }); this.setState({ カウント: this.state.count + 1 }, () => { console.log('2:', this.state.count); }); this.setState({ カウント: this.state.count + 1 }, () => { console.log('3:', this.state.count); }); console.log(この状態の数); }, 0); } 与える() { 戻る ( <div スタイル={{ 幅: '100px'、 高さ: '100px'、 背景色: "黄色" }}> {この州数} </div> ) } } デフォルトのテストをエクスポートします。 現時点で印刷された結果は次のとおりです。
setState の最初のパラメータを関数に置き換えます。 コンポーネントマウント() { タイムアウトを設定する(() => { this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('1', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('2', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('3', this.state.count); }); console.log(この状態の数); }, 0); } 印刷結果は上記と同じです。 状態は完全に制御可能だと感じますか? setTimeout では、複数の setState 呼び出しが有効になり、更新された状態は各 setState の 2 番目のパラメータで取得できます。 同様に、ネイティブ イベントの出力結果は setTimeout の出力結果と一致し、同期されます。 React をインポートします。{ コンポーネント } から 'react' をインポートします。 クラス Test は Component を拡張します { コンストラクタ(props) { スーパー(小道具); この状態 = { カウント: 1 }; } コンポーネントマウント() { document.body.addEventListener('click', this.handleClick, false); } コンポーネントのマウントを解除します(){ document.body.removeEventListener('click', this.handleClick, false); } ハンドルクリック = () => { this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('1', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('2', this.state.count); }); this.setState((前の状態、props) => { 戻り値: 前の状態のカウント + 1 } }, () => { console.log('3', this.state.count); }); console.log(この状態の数); } 与える() { 戻る ( <div スタイル={{ 幅: '100px'、 高さ: '100px'、 背景色: "黄色" }} > {この州数} </div> ) } } デフォルトのテストをエクスポートします。 setState関連のソースコード以下のコードはすべてreact17.0.2バージョンのものです ディレクトリ ./packages/react/src/ReactBaseClasses.js 関数 Component(props, context, updater) { プロパティ this.context = コンテキスト; // コンポーネントに文字列参照がある場合は、後で別のオブジェクトを割り当てます。 this.refs = 空のオブジェクト; // デフォルトのアップデータを初期化しますが、実際のアップデータは // レンダラー。 this.updater = アップデーター || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = {}; Component.prototype.setState = function(partialState, callback) { 不変 partialState の型 === 'オブジェクト' || partialState の type === 'function' || 部分状態 == null、 'setState(...): 更新する状態変数のオブジェクトまたは' + '状態変数のオブジェクトを返す関数。', ); this.updater.enqueueSetState(this、partialState、コールバック、'setState'); }; setState は 2 つのパラメータを受け取ることができます。最初のパラメータはオブジェクト、関数、null、または undefined にすることができ、エラーはスローされません。以下の this.updater.enqueueSetState メソッドを実行します。 enqueueSetState をグローバルに検索し、2 つのディレクトリでこの変数を見つけます。 まず、最初のディレクトリ セット: ディレクトリ ./packages/react/src/ReactNoopUpdateQueue.js の 100 行目の enqueueSetState メソッドでは、パラメーターは this、初期化された状態、コールバック、および文字列 setState です。this は現在の React インスタンスを参照します。 enqueueSetState: 関数( パブリックインスタンス、 部分的な状態、 折り返し電話、 発信者名、 ){ warnNoop(publicInstance, 'setState'); } 次に、 warnNoop メソッドを見てみましょう。 const didWarnStateUpdateForUnmountedComponent = {}; 関数 warnNoop(publicInstance, callerName) { __DEV__ の場合 { const コンストラクター = publicInstance.constructor; const コンポーネント名 = (コンストラクター && (コンストラクター.displayName || コンストラクター.name)) || 'ReactClass'; const warningKey = `${componentName}.${callerName}`; if (didWarnStateUpdateForUnmountedComponent[警告キー]) { 戻る; } コンソール.エラー( 「まだマウントされていないコンポーネントでは %s を呼び出すことはできません。」 + 「これは何も起こりませんが、アプリケーションにバグがあることを示している可能性があります。」 + '代わりに、`this.state`に直接割り当てるか、`state = {};`を定義します' + '%s コンポーネント内の目的の状態を持つクラス プロパティ。', 発信者名、 コンポーネント名、 ); マウントされていないコンポーネントに対してWarnStateUpdateを実行しました[警告キー] = true; } } このコードは、didWarnStateUpdateForUnmountedComponent オブジェクトに属性を追加することと同じです。属性のキーは、現在 setState.setState になっている React コンポーネントです。この属性が存在する場合は、それが返されます。この属性が存在しないか、この属性の値が false の場合は、この属性の値は true に設定されます。 別のディレクトリを見てみましょう: ディレクトリ ./react-reconciler/src/ReactFiberClassComponent.new.js および ReactFiberClassComponent.old.js const クラスコンポーネントアップデート = { enqueueSetState(inst、ペイロード、コールバック) { const ファイバー = getInstance(inst); 定数イベント時間 = requestEventTime(); 定数レーン = requestUpdateLane(ファイバー); 定数 update = createUpdate(eventTime, レーン); update.payload = ペイロード; if (コールバック !== 未定義 && コールバック !== null) { __DEV__ の場合 { 無効なコールバックが発生した場合に警告します(コールバック、'setState'); } update.callback = コールバック; } enqueueUpdate(ファイバー、更新、レーン); const ルート = scheduleUpdateOnFiber(ファイバー、レーン、イベント時間); ルートが null の場合 entangleTransitions(ルート、ファイバー、レーン); } __DEV__ の場合 { デバッグトレースを有効にする場合 if (fiber.mode & DebugTracingMode) { const name = getComponentNameFromFiber(fiber) || '不明'; logStateUpdateScheduled(名前、レーン、ペイロード); } } } スケジューリングプロファイラーを有効にする場合 markStateUpdateScheduled(ファイバー、レーン); } } } 主な機能は enqueueUpdate です。
エクスポート関数 enqueueUpdate<State>( 繊維: 繊維、 更新: Update<状態>、 レーン: レーン、 ){ 定数 updateQueue = fiber.updateQueue; 更新キューが null の場合 // ファイバーがマウント解除されている場合にのみ発生します。 戻る; } const sharedQueue: SharedQueue<State> = (updateQueue: any).shared; if (isInterleavedUpdate(ファイバー、レーン)) { const インターリーブ = sharedQueue.interleaved; if (インターリーブ === null) { // これは最初の更新です。循環リストを作成します。 更新.next = 更新; // 現在のレンダリングの終了時に、このキューのインターリーブ更新は // 保留キューに転送されます。 共有キューをプッシュします。 } それ以外 { インターリーブされた interleaved.next = 更新; } sharedQueue.interleaved = 更新; } それ以外 { const pending = sharedQueue.pending; if (保留中 === null) { // これは最初の更新です。循環リストを作成します。 更新.next = 更新; } それ以外 { 更新.next = pending.next; pending.next = 更新; } sharedQueue.pending = 更新; } __DEV__ の場合 { もし ( 現在処理中のキュー === 共有キュー && !didWarnUpdateInsideUpdate ){ コンソール.エラー( '更新 (setState、replaceState、または forceUpdate) がスケジュールされました ' + '更新関数内から。更新関数は純粋でなければなりません。' + ' 副作用はありません。componentDidUpdate または ' + の使用を検討してください。 '折り返し電話。'、 ); 更新中に警告を発した = true; } } } これを見ると、このメソッドはこの更新の更新を更新キューに追加しますが、このバージョンでは isBatchingUpdates プロパティが見つからないことがわかります。 React Fiber への変更はかなり大きいようですので、とりあえずここで止めて、何か新しいことが見つかったら追加していきます。 要約する
以上が react setState の詳しい説明です。 react setState についてさらに詳しく知りたい方は、123WORDPRESS.COM 内の他の関連記事もぜひご覧ください! 以下もご興味があるかもしれません:
|
>>: Docker を使って LEMP 環境を素早く構築する方法の例
タオバオが、ダブル11に最も多くの注文をした2人のユーザー、ユーザー1:「ショッピングの皇帝、陳哈哈...
序文vue3 を使った例をいくつか書いてみましたが、Vue3 のコンポジション API はよく設計さ...
コードをコピーコードは次のとおりです。 <form action="/hehe&qu...
ターミナル分割画面ツールは2つあります: screen と tmux 1. 画面分割を使用する(上下...
目次1. 証明書を生成する2. リモートを有効にする3. リモート接続3.1 Jenkins接続3....
1. システムにログインし、ディレクトリに入ります: cd /etc/sysconfig/netwo...
目次単一コンテンツ投影マルチコンテンツ投影単一条件のコンテンツ投影アプリ-人物-htmlアプリ担当者...
目次Nginx 負荷分散構成Nginx 負荷分散戦略ポーリング(デフォルト)重さip_ハッシュ公正(...
組織内で何らかのパスワード ポリシーがすでに実装されている場合は、この記事を読む必要はありません。た...
準備する: MySQL 8.0 Windows zip パッケージのダウンロード アドレス: htt...
目次概要インデックスデータ構造バイナリツリー赤黒木BツリーB+ツリーハッシュ索引InnoDB インデ...
* 住所 - 住所* blockquote - ブロック引用* center - 中央揃えブロック*...
環境の準備操作を開始する前に、hadoop バージョンがインストールされていることを確認してください...
以前、フロントエンド技術グループに所属していたとき、グループのメンバーが面接中に問題に遭遇したと言っ...
目次CentOS rpm のインストールと Nginx の設定導入rpm パッケージのインストールサ...