React学習 その4 : stateを作成する(ステートフルコンポーネント)

前回の記事では、propsについて勉強できました。

前回の記事でも冒頭記述したように、復習(忘れないように)でもう一度記述しようと思います。

Props(プロップス):

  • propsは、親コンポーネントから子コンポーネントにデータを渡すために使用される。
  • propsは読み取り専用であり、コンポーネント内で変更することはできない。
  • propsは外部から渡されるため、コンポーネント内部では不変となる。
  • 親コンポーネントが子コンポーネントにデータを渡すためのメカニズムとして使用され、コンポーネント間のデータの受け渡しに便利。

気になったのが、stateも同じデータを管理するものだったと思うので、

State(ステート):

  • stateは、コンポーネント内部で管理されるデータの状態を表現する。
  • stateはコンポーネントによって変更可能であり、setStateメソッドを使用して更新することができる。
  • stateはコンポーネントに固有であり、そのコンポーネント自体が保持するデータになる。
  • stateの変更によって、Reactはコンポーネントを再レンダリングして、変更をUIに反映する。

propsとstateの違いとしては、

propsは外部から渡される読み取り専用のデータであり、コンポーネント内で変更することはできないようです。一方、stateはコンポーネント自体が管理する可変のデータであり、コンポーネント内で変更が可能となるようです。

例えば、親コンポーネントから子コンポーネントにユーザー名を渡す場合、propsを使用するようでした。ユーザーがボタンをクリックしたときに表示されるテキストを変更する場合、stateを使用してコードを記述していました。

復習ができたところで、今回は、Stateについての記事を書こうと思います。

ステートフルコンポーネントを作成する

自分でまとめた上記の説明プラス学んだことを記述してみます。

state (状態、ステート) は、アプリケーションが認識する必要のあるデータで構成され、時間とともに変化する可能性があります。 アプリでは通常、必要に応じて状態の変化に応答し、更新された UI を表示します。 React は、最新のウェブアプリケーションの状態管理に適したソリューションを備えています。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/create-a-stateful-component

React コンポーネントで state を作成するには、constructor のコンポーネントクラスで state プロパティを宣言します。 これにより、コンポーネントが作成時に state で初期化されます。 state プロパティは JavaScript の object に設定する必要があります。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/create-a-stateful-component

下記が、stateプロパティでのオブジェクト設定

this.state = {

}

コンポーネントが存在している間は、state オブジェクトにアクセスすることができます。 更新したり、UI にレンダーしたり、props として子コンポーネントに渡したりすることができます。 state オブジェクトは必要に応じて単純なものにも複雑なものにもすることができます。 こうした state を作成するには、React.Component を拡張してクラスコンポーネントを作成する必要があります。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/create-a-stateful-component

ユーザーインターフェイスに state をレンダーする

コンポーネントの初期状態を定義したら、レンダーされる UI に、そのコンポーネントの任意の部分を表示することができました。
コンポーネントがステートフルの場合は、コンポーネントから常に自身の render() メソッドの中で state のデータにアクセスできるようです。 データへのアクセスには this.state を使用できました。

render メソッドの return の中で state 値にアクセスする場合は、値を中括弧で囲む必要があるようです。

 React では、仮想 DOM と呼ばれるものを使用して、見えないところで変更を追跡しているようです。 state のデータが更新されると、データを prop として受け取った子コンポーネントを含めて、そのデータを使用しているコンポーネントの再レンダーをトリガーするようです。
実際の DOM も更新しますが、必要な場合に限られるようでした。
つまり、DOM の変更を気にする必要はない、ということらしいです。
単に UI をどのように表示させるかを宣言するだけみたいです。

また、あるコンポーネントをステートフルにした場合、他のコンポーネントはその state を認識しないことに注意しないといけないようです。
state データを props として子コンポーネントに渡さない限り、state は完全にカプセル化されるか、またはそのコンポーネントに対してローカルになるから見たいです。
state のカプセル化という考え方はとても重要なようです。
なぜなら、特定のロジックを記述して、そのロジックをコード内の 1 か所に閉じ込めて分離しておけるからです。

まとめると、

  • stateは、1つのコンポーネント内でしか、利用できないということ。
  • 仮に他のコンポーネントでも使用したい場合は、propsを使用して子コンポーネントとしてデータを渡す必要がある。

別の方法でユーザーインターフェイスに state をレンダーする

別の方法でコンポーネントの state にアクセスすることもできるそうです。
render() メソッドで、return ステートメントの前に直接、JavaScript を記述することができました。 たとえば、関数を宣言したり、state や props のデータにアクセスしたり、そのデータに対して計算を実行したりできます。
任意のデータを変数に割り当てて、return ステートメントでアクセスすることができます。

returnメソッドより上では、JavaScript を直接記述できるので、この参照を中括弧で囲む必要はありません。

this.setState を使用して state を設定する

ここでは、コンポーネントの state を変更する方法を学びました。
React には、コンポーネントの state を更新するための setState というメソッドが用意されているようです。setState メソッドはコンポーネントクラスの中で this.setState() のように呼び出し、キーと値のペアを持つオブジェクトを渡します。
キーは state のプロパティであり、値は更新された state データです。 たとえば、username を state に保存していて、それを更新したい場合は、次のようにします。

this.setState({
  username: 'Lewis'
});

 また、React ではパフォーマンスを向上させるために複数の state の更新がバッチ処理されることがあります。 このため、setState メソッドによる state の更新が非同期になる可能性があります。 この問題を回避する方法として、setState メソッドの代わりとなる構文があります。 必要になることはめったにありませんが、覚えておくと役に立ちます。

 また、React ではパフォーマンスを向上させるために複数の state の更新がバッチ処理されることがあります。 このため、setState メソッドによる state の更新が非同期になる可能性があります。 この問題を回避する方法として、setState メソッドの代わりとなる構文があります。 必要になることはめったにありませんが、覚えておくと役に立ちます。

詳細については freeCodeCamp の React に関する記事を参照してください。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/set-state-with-this-setstate

と書かれていたのですが、よくわからなかったので、調べてみたところ。

下記のようなことがわかりました。

setState メソッドの代わりに使用できる構文として、関数形式の setState を利用する方法があ流ようです。この方法を使用することで、state の更新が非同期になる可能性を回避することができます。

通常の setState メソッドでは、state の更新は非同期で行われるため、連続して複数回の状態更新を行う場合に予期しない結果が生じる可能性があるようです。これは、React がパフォーマンスの最適化のために、複数の state 更新をまとめてバッチ処理するためのようです。

関数形式の setState を使用すると、更新する状態を前の状態に依存させることができます。これにより、最新の状態を保証しながら連続して状態更新を行うことができます。

以下は、関数形式の setState の例です:

this.setState((prevState) => {
  return { count: prevState.count + 1 };
});

この例では、prevState を引数として受け取り、更新後の状態を返す関数を渡しています。この関数内で状態の更新を行うことができます。React はこの関数を同期的に実行し、前の状態を最新の状態として扱うようです。

関数形式の setState を使用することで、連続して状態の更新を行う際に正確な結果を得ることができきるようです。ただし、注意点として、関数形式の setState は非同期ではなくなるため、パフォーマンス上の理由で状態の更新がバッチ処理されることはありません。そのため、特定のタイミングでの最新の状態が必要な場合には、コールバックや useEffect の依存リストなどの他の手法を検討する必要があるそうです。

ちなみに、最近のReactでは、useEffectがよく使われているそうでした。

useEffectフックは、コンポーネントのライフサイクルイベントや副作用を扱うためのフックです。

従来のReactクラスコンポーネントでは、※ライフサイクルメソッド(componentDidMount、componentDidUpdate、componentWillUnmountなど)を使用して副作用やデータの取得を行っていました。しかし、関数コンポーネントではライフサイクルメソッドが使用できないため、代わりにuseEffectフックを使用することが推奨されています。

useEffectフックは、コンポーネントがレンダリングされた後に実行され、コンポーネントが再レンダリングされるたびにも実行されます。主な目的は、以下のような副作用を処理することです:

  1. データの取得やAPIの呼び出し
  2. イベントリスナーの設定や解除
  3. タイマーの設定や解除
  4. 外部ライブラリの初期化やクリーンアップ

useEffectフックは、第1引数に副作用を含む関数を受け取ります。この関数は副作用を実行し、必要な場合にはクリーンアップ関数を返すこともできます。

以下は、useEffectフックの基本的な使用例です:

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // 副作用の処理

    return () => {
      // クリーンアップ関数(必要な場合)
    };
  }, []); // 依存リスト(変更を監視する状態)

  // コンポーネントの描画
}

第2引数に依存リストを指定することで、特定の状態の変更にのみ反応するように制御することもできます。依存リストが空の場合、副作用は最初のマウント時にのみ実行され、コンポーネントのアンマウント時にクリーンアップ関数が呼び出されます。

useEffectフックは、コンポーネントの副作用の管理やリソースの解放など、様々なシナリオで役立ちます。適切に使用することで、コンポーネントのパフォーマンスやメモリリークの問題を回避しながら、柔軟で効率的なコードを記述することができます。

※ライフサイクルメソッド:Reactコンポーネントの特定の時点で呼び出されるメソッドのことです。これらのメソッドを使用することで、コンポーネントの作成、更新、破棄などの異なるフェーズでカスタムの動作や処理を追加することができるようです。(ライフサイクルメソッドは、最近はあまり使われないそうです。)

クラスメソッドに ‘this’ をバインドする

state の設定と更新に加えて、コンポーネントクラスのメソッドを定義することもできるようです。
クラスメソッドでは通常、メソッドのスコープ内でクラスのプロパティ (state や props など) にアクセスできるように、this キーワードを使用する必要があ流ようです。
いくつかの方法でクラスメソッドから this にアクセスすることができます。

よく使用される方法として、コンストラクターで this を明示的にバインドすることでできるようです。この場合、コンポーネントの初期化時に this がクラスメソッドにバインドされます。

バインド:

バインド(Bind)は、JavaScriptにおいて、関数と特定のオブジェクトを結びつけることを指すようです。これにより、関数内の this キーワードが特定のオブジェクトを参照するようになります。

通常、関数内で this を使用する場合、その関数がどのオブジェクトに属しているかによって this の値が変わります。しかし、関数を別のコンテキストで使用する場合や、イベントハンドラーとして使用する場合には、this の値が予期しないものになることがあるようです。

これを回避するために、bind メソッドを使用して関数を特定のオブジェクトにバインドすることができるようです。bind メソッドは、関数が呼び出されたときに this が指すべきオブジェクトを明示的に指定します。

以下が、バインドの例です。

const person = {
  name: 'John',
  sayHello: function() {
    console.log(`Hello, ${this.name}!`);
  }
};

const sayHello = person.sayHello.bind(person);
sayHello(); // "Hello, John!"

この例では、person オブジェクト内の sayHello メソッドを bind メソッドを使って person オブジェクトにバインドしました。これにより、sayHello 関数が呼び出されたときに thisperson オブジェクトを参照するようになります。

bind メソッドを使用することで、関数が正しいコンテキストで実行されることが保証されます。これにより、関数を他のコンテキストで使用する場合や、クラスコンポーネントのイベントハンドラーとして使用する場合などで便利だそうです。

state を使用して要素を切り替える

stateを更新する際に、更新する前のstateの値を知る必要がありました。

ただ、stateの更新が非同期であった場合、Reactが複数のsetState関数の呼び出しを単一に更新するようになります。(バッチ処理の影響)

なので、次の値を計算する際に、this.state や tihs.props を使用した前の値に頼れなくなります。
そのため、

this.setState({
  counter: this.state.counter + this.props.increment
});

上記のコードの場合、this.statethis.propsの値は現在の値を参照します。しかし、非同期的な更新のため、このコードが実行された後、this.statethis.propsが変更されている可能性があります。そのため、意図した通りに状態を更新することができない場合があります。

つまり、上記のような、コードは使用しないようになっています。

では、代わりにどうするのか?

stateやpropsにアクセスできる関数をstateに渡すことで解決できるようです。

どういうことかというと、

コールバック関数を使用すると、

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

this.setStateの引数として渡された関数は、最新の状態とプロパティを参照することができます。コールバック関数内では、stateパラメータは現在の状態を、propsパラメータは現在のプロパティを表します。そのため、常に最新の値を使用して状態を計算することができます。

このようにコールバック関数を使用することで、非同期の状態更新に対してより正確に対応することができるようになるからだそうです。

シンプルなカウンターを記述する

ここで、今までのまとめのような感じで課題がありました。

問題は、

state で count の値を追跡する Counter コンポーネントがあります。 2 つのボタンがあり、increment() メソッドと decrement() メソッドを呼び出します。 対応するボタンがクリックされたときにカウンターの値が 1 ずつ増加または減少するように、これらのメソッドを記述してください。 また、リセットボタンをクリックしたときにカウントを 0 に設定する reset() メソッドを作成してください。

注: ボタンの className は変更しないでください。 また、コンストラクターで新しく作成されるメソッドに必要なバインディングを忘れずに追加してください。

とのことでした。

下記やったことの内容とコードを共に記述しておきます。

※つまずいた点

this.setStateメソッドの引数としてアロー関数を使用する場合、アロー関数がオブジェクトを返すことを示すために、()で囲むことが一般的なようでした。'()’がないとエラーが出た、、、

以下のコードのところです。:

this.setState((prevState) => (
  { count: prevState.count - 1 }
))

この場合、this.setStateの引数として渡されたアロー関数は、前の状態(prevState)を受け取り、新しい状態をオブジェクトとして返しています。オブジェクトは波括弧 {} で囲まれており、その中に新しい状態のプロパティであるcountとその値を定義しています。

this.setStateメソッドは、この返されたオブジェクトを使用して状態を更新します。

()で囲まれたオブジェクトは、アロー関数の本体であることを明示的に示しています。これにより、オブジェクトを返すことが意図されていることを明確にします。

制御された入力を作成する

お問い合わせフォームなので、ユーザーが入力するところで制御可能なフォームにする際に今回勉強したことが役に立つそうです。

アプリケーションによっては、レンダーされた UI と state との間でもっと複雑なやり取りをする場合があります。 たとえば、input や textarea などのテキスト入力のフォームコントロール要素は、DOM 内ではユーザータイプとして独自の状態を維持します。 React では、こうしたミュータブルな状態の扱いを React コンポーネントの state に移すことができます。 ユーザーの入力はアプリケーションの state の一部となり、その入力フィールドの値は React によって制御されます。 通常は、ユーザー入力が可能な入力フィールドを持つ React コンポーネントがある場合、それは制御された入力フォームになります。

https://www.freecodecamp.org/japanese/learn/front-end-development-libraries/react/create-a-controlled-input

課題としては、

コードエディターに、制御された input 要素を作成するための ControlledInput というコンポーネントのスケルトンがあります。 コンポーネントの state は、空文字列を保持する input プロパティですでに初期化されています。 この値は、ユーザーが input フィールドに入力するテキストを表します。

まず、event というパラメーターを持つ handleChange() というメソッドを作成してください。 このメソッドが呼び出されると、input 要素からのテキスト文字列を含む event オブジェクトを受け取ります。 この文字列にはメソッドの中で event.target.value を使用してアクセスできます。 コンポーネントの state の input プロパティを、この新しい文字列に更新してください。

render メソッドで、h4 タグの上に input 要素を作成してください。 コンポーネントの state の input プロパティと等しい value 属性を追加してください。 そして、onChange() イベントハンドラーを追加して handleChange() メソッドに設定してください。

入力ボックスに入力すると、そのテキストは、ローカルの state の input プロパティとして設定された handleChange() メソッドによって処理され、ページ上の input ボックスの値としてレンダーされます。 コンポーネントの state は、入力データに関して単一の信頼できる情報源になります。

最後に、必要なバインディングを忘れずにコンストラクターに追加してください。

下記がそのコードになります、

「スケルトン」とは、デザインやレイアウトのフレームワークの一部として使用される、コンテンツがない状態のプレースホルダーのことを指すようです。スケルトンは、Webページやモバイルアプリのローディングや遅延読み込み時に、ユーザーに視覚的なフィードバックを提供するために使用されるようです。

※気になったところ

handleChange関数の第一引数eventって必要なのかどうか?

handleChange関数の第一引数としてeventが必要なのは、イベントハンドラとして機能するためです。Reactでは、イベントハンドラ関数には通常、イベントオブジェクトが渡されます。

<input>要素にonChangeイベントが発生したとき、handleChange関数が呼び出されます。このとき、eventオブジェクトはそのイベントに関連する情報を保持しており、event.target.valueを使用することで入力フィールドの値にアクセスできます。

具体的には、以下の部分です:

handleChange = (event) => {
  this.setState((prevState) => ({
    input: event.target.value
  }));
}

event.target.valueは、イベントが発生した要素の値を取得するために使用されます。input要素の値が変更されるたびに、新しい値がevent.target.valueとして取得され、それがinputという状態のプロパティに設定されます。

そのため、event引数はhandleChange関数内で使用され、入力フィールドの値を取得するために必要となります。

jsでも同様だったかどうか?

JavaScriptでも同様の概念が適用されます。Reactのイベントハンドラ関数には、通常、イベントオブジェクトが渡されます。JavaScriptのイベントモデルでは、イベントハンドラ関数が呼び出されるときに、イベントオブジェクトが自動的に渡されるようになっています。

例えば、以下のようなJavaScriptのイベントハンドラ関数を考えてみましょう:

function handleChange(event) {
  console.log(event.target.value);
}

この場合、eventオブジェクトはイベントが発生したときの情報を保持しており、event.target.valueを使用して入力フィールドの値にアクセスできます。

Reactの場合と同様に、JavaScriptのイベントハンドラ関数でも、event.targetはイベントが発生した要素を参照し、event.target.valueはその要素の値を取得します。

したがって、JavaScriptでも同じ原則が適用されるため、event引数はイベントハンドラ関数内で使用され、要素の値にアクセスするために必要となります。

呼び出す際に引数はいるのか?

JavaScriptのイベントハンドラ関数を呼び出す際には、仮引数を使用することはないようでした。イベントハンドラ関数はイベントが発生した際に自動的に呼び出されるため、明示的な関数呼び出しの際に引数を指定する必要もなかったようです。

以下は一般的な例です:

// イベントハンドラ関数の定義
function handleChange(event) {
  console.log(event.target.value);
}

// イベントリスナーに関数を登録
document.getElementById("myInput").addEventListener("input", handleChange);

この例では、handleChange関数をaddEventListenerメソッドに渡してイベントリスナーとして登録しています。イベントが発生すると、関数が自動的に呼び出され、イベントオブジェクトがeventとして渡されます。

Reactの場合、JSX内でイベントハンドラ関数を指定する場合には、通常はアロー関数や.bind()メソッドを使用して関数をバインドします。この場合、関数がイベント発生時に自動的に呼び出されるため、明示的な関数呼び出しの際に引数を指定する必要はありません。

例えば、次のように書かれたReactのコードでは、関数のバインドを行っています:

class MyComponent extends React.Component {
  handleChange(event) {
    console.log(event.target.value);
  }

  render() {
    return (
      <input onChange={this.handleChange.bind(this)} />
    );
  }
}

この例では、onChangeイベントでhandleChange関数が呼び出されます。関数のバインドが行われるため、関数がイベント発生時に自動的に呼び出され、イベントオブジェクトがeventとして渡されます。

したがって、イベントハンドラ関数を呼び出す際には、通常は引数を指定する必要はありませんでした。イベント発生時に自動的にイベントオブジェクトが渡されていました。

制御されたフォームを作成する

inputやtextAreaなどの要素で内部状態を制御できることを学びました。

なので、通常のHTML form要素を含むフォーム要素にも適用されるようです。

ここで、まとめ課題がありました。

submit ハンドラーを持つ空の form で設定された MyForm コンポーネントがあります。 submit ハンドラーはフォームの送信時に呼び出されます。

フォームを送信するボタンはすでに追加してあります。 その type は submit に設定されていて、フォームを制御するボタンであることを示しています。 form に input 要素を追加し、前回のチャレンジのように、その value と onChange() 属性を設定してください。 次に、handleSubmit メソッドで、コンポーネントの state プロパティ submit をローカルの state の現在の入力値に設定して、メソッドを完成させてください。

注: submit ハンドラーでは、デフォルトのフォーム送信動作が実行されてウェブページが更新されるのを防ぐために、event.preventDefault() を呼び出す必要もあります。 ここでは簡単のため、更新によるチャレンジコードのリセットを防ぐために、デフォルトの動作は無効になっています。

さらに、form の後に、コンポーネントの state の submit 値をレンダーする h1 タグを作成してください。 これで、フォームに入力してボタンをクリックすると (または enter を押すと)、入力がページにレンダーされます。

下記が解答になります。

今回はここまでになります。

最近のReactでは、型宣言はTypeScriptで行うようでした。

また、フォームもある程度こちらで制御できるのもすごく助かります。

本日はここまで〜

タイトルとURLをコピーしました