10分でReactsetState()を使ってプロになる方法

この記事は、Reactに最初のアプローチをすでに持っていて、初心者として、どのようにsetState機能し、どのように正しく使用するかについて疑問を持っている人々を対象としています。また、中級から上級の開発者が状態を設定するためのよりクリーンで抽象化された方法を使用し、高階関数が状態を処理および抽象化するのに役立つはずです。

読んで楽しんでください!

だから、コーヒーを飲み、読み続けてください!?

setState()の基本概念

Reactコンポーネントを使用すると、ユーザーインターフェイス(UI)を独立した再利用可能な部分に分割できるため、各部分を個別に考えることができます。

概念的には、コンポーネントはJavaScript関数のようなものです。それらは任意の入力(「小道具」と呼ばれる)を受け入れ、画面に何を表示するかを説明するReact要素を返します。

ユーザーに何かを入力する機会を与える必要がある場合、またはコンポーネントが小道具として受け取る変数を何らかの方法で変更する必要がある場合は、が必要になりsetStateます。

コンポーネントを関数として宣言するかクラスとして宣言するかにかかわらず、コンポーネントが独自の小道具を変更してはなりません。

すべてのReactコンポーネントそれらの小道具に関して純粋関数のように振る舞わなければなりません。これは、入力を変更しようとせず、同じ入力に対して常に同じ結果を返す関数を意味します。

もちろん、アプリケーションUIは動的であり、時間の経過とともに変化します。それstateが作成された理由です。

State このルールに違反することなく、Reactコンポーネントが、ユーザーアクション、ネットワーク応答、およびその他の応答に応じて、時間の経過とともに出力を変更できるようにします。

クラスとして定義されたコンポーネントには、いくつかの追加機能があります。ローカル状態は、クラスコンポーネントでのみ使用できる機能です。

setState は、ライブラリで提供されるAPIメソッドであり、ユーザーは時間の経過とともに状態を定義および操作できます。

setState()を使用する場合の3つの経験則

状態を直接変更しないでください

状態の更新は非同期である可能性があります

ReactはsetState()、パフォーマンスのために複数の呼び出しを1つの更新にバッチ処理する場合があります。

this.props this.stateは非同期で更新される可能性があるため、次の状態を計算するためにそれらの値に依存しないでください。

あなたは、常に供給、機能的なアプローチでこの種の操作を行う必要がありますstateし、propsそして新しいを返すstate元に基づきます。

状態の更新がマージされます

を呼び出すとsetState()、Reactは指定したオブジェクトを現在のオブジェクトにマージしますstate

以下の例dogNeedsVaccinationでは、他のstate変数とは独立して変数を更新しています。

マージは浅いためthis.setState({ dogNeedsVaccination: true }) 、他の変数はそのまま残し、の値のみを置き換えますdogNeedsVaccination

データフローを尊重し、最大値を述べないようにします

データが流れ落ちる!親コンポーネントも子コンポーネントも、特定のコンポーネントがステートフルであるかステートレスであるかを知ることはできず、関数として定義されているかクラスとして定義されているかを気にする必要はありません。

そのためstate、ローカルまたはカプセル化と呼ばれることがよくあります。それを所有および設定するコンポーネント以外のコンポーネントにはアクセスできません。

あなたの場合にはsetState小道具やコンポーネントでそれを使用するには、レンダリングの小道具の流れを壊しています。何らかの理由で、コンポーネントに渡された小道具が親コンポーネントで変更された場合、子は自動的に再レン​​ダリングされませんか?!

例を確認しましょう:

ここにHome、1000msごとにマジックナンバーを生成し、それを独自に設定するコンポーネントがありますstate

その後、番号をレンダリングし、Child3つの異なるアプローチを使用して表示する目的でマジックナンバーを受け取る3つのコンポーネント(兄弟)を呼び出します。

最初のアプローチ

コンポーネントChildOfHomeはReact小道具のカスケードフローを尊重しており、目的はマジックナンバーを表示することだけであることを考慮して、props受信したものを直接レンダリングしています。

2番目のアプローチ

コンポーネントChildOfHomeBrotherpropsその親からを受け取り、を呼び出してcomponentDidMount、マジックナンバーをに設定しstateます。次に、をレンダリングしstate.magicNumberます。

この例はrender()prop が変更されたことを認識していないため機能しません。そのため、コンポーネントの再レンダリングはトリガーされません。コンポーネントは再レンダリングされなくなったため、componentDidMount呼び出されず、表示は更新されません。

3番目のアプローチ

通常、2番目のアプローチを使用してそれを機能させようとすると、何かが欠けていると思います。一歩後退する代わりに、コードに何かを追加して機能させるようにします。

したがって、この3番目のアプローチでは、コンポーネントの再レンダリングをトリガーするcomponentDidUpdateための変更があるかどうかを確認するために追加しましたprops。これは不要であり、コードがクリーンになりません。また、パフォーマンスコストが発生します。これは、チェーンされたコンポーネントや副作用が多数ある大きなアプリでこれを行う回数で乗算されます。

ユーザーが受け取ったprop値を変更できるようにする必要がない限り、これは間違っています。

prop値を変更する必要がない場合は、常にReactフロー(最初のアプローチ)に従って動作し続けるようにしてください。

グリッチで私が用意したこの例で、動作中のWebページを確認できます。見て楽しんでください?

また、この記事に関する私のリポジトリのHome.jsand HomeCodeCleaned.js(HTMLのものなし)のコードも確認してください。

setStateの方法

ですから、この時点で、手を汚す時が来たと思います!

少し遊んで、setStateそれを改善しましょう!ついてきて、もう一杯コーヒーを飲みましょう!

ユーザーデータを更新するための小さなフォームを作成しましょう。

上記の例のコードは次のとおりです。

stateオブジェクトとして設定していますが、現在の状態は前回の状態に依存しないので問題ありません。

姓を紹介して表示するフォームフィールドをもう1つ作成するとどうなりますか?

いいね!handleFormChangeすべての入力フィールドとを処理できるようにメソッドを抽象化しましたsetState

データを有効または無効としてマークするトグルボタンと、状態に加えた変更の数を知るためのカウンターを追加するとどうなりますか?

うん!私たちは揺れています!私たちはたくさんのものを抽象化しました!

うーん…isValid変数を制御するためのチェックボックスではなく、単純なトグルボタンが必要だとしましょう。

また、このメソッドからカウンターハンドラーを分離しましょう。これはうまく機能しますが、Reactが変更をバッチ処理/グループ化する必要があるより複雑な状況では、this.state.counter変数に依存して変更を追加することは適切なポリシーではありません。この値は、ユーザーが気付かないうちに変更される可能性があります。

操作が呼び出された瞬間にその浅いコピーを使用していますが、その特定の時点では、その値が期待した値であるかどうかがわかりません。

少し機能的に行きましょう!

わかりました—ハンドラーを分離したために抽象化が失われましたが、それは正当な理由です!

したがって、この時点ではhandleFormChange、オブジェクトをsetStateAPIメソッドに渡し続けます。しかし、handleCounterandhandleIsValidメソッドは機能するようになり、現在の状態を取得することから始め、その状態に応じて、次の状態に変更します。

これは、state前の状態に依存する変数のを変更する正しい方法です。

変更が発生するたびにフォームと入力フォームのconsole.log()変更を記述したい場合はどうなりますか?やるだけやってみよう!firstNamelastName

いいね!handleFormChange発生するたびに(つまり、新しいキーが押された場合)、logFields()メソッドが呼び出され、現在の状態がコンソールに記録されます。

ブラウザコンソールを確認してみましょう。

待つ!ここで何が起こったのですか?コンソールログは、現在のフォーム入力の1つ前の変更です。なんでこんなことが起こっているの?

setStateは非同期です!!

私たちはすでにこれを知っていましたが、今私たちはそれを私たちの目で見ています!そこで何が起こっているのですか?上記のhandleFormChangelogFieldsメソッドを見てみましょう。

したがって、handleFormChangeメソッドはイベント名と値を受け取り、setStateこのデータを処理します。次に、を呼び出してhandleCounterカウンタ情報を更新し、最後にlogFieldsメソッドを呼び出します。このlogFieldsメソッドはを取得し、currentState「Eduardo」ではなく「Eduard」を返します。

setState重要なのは、非同期であり、現時点では動作しないということです。Reactはその仕事をしていて、logFields最初にメソッドを実行setStateし、次のイベントループに向けて出発します。

しかし、どうすればこのような状況を回避できますか?

さて、setStateAPIにはcallbackこの状況を回避するためのがあります:

logFields()状態に加えた最近の変更を考慮に入れる場合は、次のようにコールバック内で呼び出す必要があります。

さて、今それは働いています!

Reactに次のように伝えています。logFieldsメソッドを呼び出すときに、stateすでに更新されていることを確認してほしいことに注意してください。あなたを信頼する!"

Reactは次のように述べています。私は通常裏庭で行うこれらすべてのバッチを物事で処理し、setStateそれが終了したときにのみ呼び出しlogFields()ます!クールな男!リラックス!"

そして実際のところ、それはうまくいきました!

みなさん、大丈夫です!この時点で、の主な落とし穴に対処しましたsetState

壁を越えて行く勇気はありますか?一杯のコーヒーを手に入れて、本当にくしゃくしゃにしましょう…

setState()でファンシーを取得する

今、私たちは持っていることhandleCounterhandleIsValidメソッド、およびsetState()関数で表現し、我々は他の機能と状態の更新を構成することができます!私は作曲が好きです!楽しもう!

内部のロジックをsetStateクラスコンポーネントの外部の関数に取り込むことができます。それを呼びましょうtoggleIsValid。☝️

これで、この関数はクラスコンポーネントの外部、アプリ内のどこにでも配置できます。

高階関数を使用するとどうなりますか?

うわー!これで、toggleIsValid関数は呼び出されなくなりました。と呼ばれる抽象的な高階関数を呼び出し、toggleKeyそれにキー(この場合は文字列)を渡します。

toggleIsValid今、どのように機能を変更する必要がありますか?

何?!これで、toggleKeyを受け取りkey、指定されたキーに応じて状態を変更する新しい関数を返すという関数が呼び出されました。

これtoggleKeyは、ライブラリまたはヘルパーファイルに含めることができます。これは、さまざまなコンテキストで呼び出して、必要な状態を反対に変更できます。

すごい!

インクリメントカウンターハンドラーでも同じことをしましょう。

うん!できます!とてもいいです。今、夢中になりましょう…

月を撃って戻る

makeUpdater適用する変換関数を受け取り、キーを受け取り、変換関数とキーを使用して状態を管理する状態関数を返す汎用関数を作成するとどうなりますか?少し混乱していますか?行こう!

さて、それで十分です…ここでやめましょう。?

このGitHubリポジトリで実行したすべてのコードを確認できます。

最後だが大事なことは

最大使用状態を回避し、Reactレンダリング小道具カスケードを尊重することを忘れないでください。

setState非同期であることを忘れないでください。

setStateオブジェクトまたは関数を取ることができることを忘れないでください

次の状態が前の状態に依存する場合は、関数を渡す必要があることを忘れないでください。

参考文献

  1. Reactドキュメント
  2. ライアン・フローレンスによる技術コースに到達することをお勧めします。

どうもありがとうございました!