状態とは何ですか?
useState フックを深く掘り下げる前に、まず用語を理解しましょう でのみ停止させることができます。
状態は、特定の時点での何かに関する情報を表します。
たとえば、テキストボックスを考えてみましょう。
最初、このテキストボックスの中には何もないので、その状態は 空の. その中に Hello と入力し始めると、キーストロークごとにテキストボックスの状態が変化します。 最初は「H」、次に「He」、「Hel」と続き、「Hello」になります。
また、入力中に以前の値が失われていないことに注意してください。 「H」に続いて「e」を押すと、「e」だけでなく「He」が表示されます。 つまり、状態は次のように考えることができます。 メモリ テキストボックスの。
React コンポーネントでの状態の必要性。
例を使ってこれを理解しましょう。
ここにあります クリックカウンター Increment ボタンがクリックされた回数を表示するコンポーネント。
私たちは使用しています ローカル変数「カウンター」 クリック数を記録します。
Increment ボタンをクリックするたびに、 ハンドルクリック 関数が呼び出されます。 この関数は、カウンターの値を 1 増やし、コンソールに値を記録します。
CodeSandbox のプレビューで [インクリメント] ボタンをクリックします。
何も起こらなかった?
私たちの論理は正しいようです。 コンソール (CodeSandbox) に記録された値は、クリックするたびに正しく更新されますが、この更新が UI に反映されないのはなぜですか?
それは、React の仕組みによるものです。
- ローカル変数を変更しても、再レンダリングはトリガーされません。
- 再レンダリング中、コンポーネントはゼロから作成されます。つまり、コンポーネントの関数 (この例では ClickCounter 関数) がもう一度実行されます。 変数 (カウンターなど) は関数に対してローカルであるため、以前の値は失われます。
では、レンダリング間でコンポーネントに値を記憶させるにはどうすればよいでしょうか?
はい、正解です! 私たちは、の助けを借りてこれを行います 使用状態 フック。
useState フック
useState フックは、状態を保持し、再レンダリングをトリガーするメカニズムを提供します。
その使い方を見てみましょう。
import React, { useState } from "react";
const state = useState(initialValue);
// OR
const state = React.useState(initialValue);
useState フックは、次の XNUMX つの項目を含む配列を返します。
- A 状態変数 再レンダリング中にその値を保持します。 useState に渡される初期値は、最初のレンダリング中に状態変数に割り当てられます。
- A セッター関数 状態変数を更新し、再レンダリングもトリガーします。
const state = useState(0);
const data = state[0];
const setData = state[1];
使い方 配列の分解 、以下に示すように、上記のステートメントを単一のステートメントにリファクタリングできます。
const [data, setData] = useState(0);
useState に渡される初期値は、最初のレンダリング中にのみ使用されます。 再レンダリングの場合は無視されます。
useState のカウンター
ここで、前のカウンターの例を更新して、useState フックを含めましょう。
- 再レンダリング間のカウンター値が必要なので、それを状態に変換しましょう。
const [counter, setCounter] = useState(0);
- handleClick 関数内で setCounter を呼び出します。
const handleClick = () => {
setCounter(counter + 1);
console.log(`%c Counter:${counter}`, "color:green");
};
setCounter 関数は、カウンター値を 1 ずつ更新し、再レンダリングをトリガーします。 再レンダリング時にコンポーネントの関数が呼び出されると、useState によって返される状態変数の値が更新されます。
更新されたコードで CodeSandbox を試してください。 [インクリメント] ボタンをクリックして、useState の魔法の動作を確認してください。
再レンダリングで、機能コンポーネントが クリックカウンター コンソール ログを表示して再度呼び出されます。 コンポーネントの先頭に追加されたログ「ClickCounter start」は、レンダリングごとに記録されます。
アップデータ機能
クリックごとにカウンターの値を 4 ずつ増やしたいとします。
const handleClick = () => {
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
console.log(`%c Counter:${counter}`, "color:green");
};
counter の初期値が 0 であると仮定します。ボタンをクリックすると、何が表示されると思いますか?
カウントが 4 になると予想していましたよね? しかし、代わりに 1 が表示されるのはなぜですか?
a) 各レンダリングは状態に関連付けられています。 その状態の値は、そのレンダリングの存続期間中ロックされたままになります。
handleClick 関数内のログでは、カウンター値が 0 として出力されることに注意してください。
setCounter メソッドを何度呼び出しても、counter の値は変わりません。
const handleClick = () => {
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
setCounter(counter + 1);
console.log(`%c Counter:${counter}`, "color:green");
};
b) イベント ハンドラー内のすべてのコードが実行されるまで、React は再レンダリングをトリガーしません。
このため、各 setCounter 呼び出しは個別のレンダリングをトリガーしません。 代わりに、React はこれらのセッター関数をキューに追加します。 キューに入れられた順序でそれらを実行します。 すべてのステートメントを実行した後に状態に加えられた更新は、次のレンダリングに反映されます。 この複数の状態更新のキューイングは、 バッチング. これにより、React のパフォーマンスが向上します。
したがって、ここでは、4 つの異なるレンダリングではなく、XNUMX つのレンダリングを取得します。
この例は単純で、以下に示すようにコードを更新することでこの問題を修正できます。
const handleClick = () => {
setCounter(counter + 4);
console.log(`%c Counter:${counter}`, "color:green");
};
しかし、次のレンダリングの前に状態を複数回更新したいユースケースがあるとしたらどうでしょう。
それが_ アップデータ _関数が便利です。
次のように、 updater 関数を使用して前の例をリファクタリングできます。
const handleClick = () => {
setCounter(prevCounter => prevCounter + 1);
setCounter(prevCounter => prevCounter + 1);
setCounter(prevCounter => prevCounter + 1);
setCounter(prevCounter => prevCounter + 1);
console.log(`%c Counter:${counter}`, "color:green");
};
ここに prevCounter ⇒ prevCounter + 1 アップデータ機能を表します。
前に説明したように、これらの updater ステートメントもキューに入れられます (バッチ処理)。
updater 関数は、次の状態を計算するために使用する保留中/前の状態を受け取ります。
以下は、アップデータ機能を追加した CodeSandbox です。 インクリメントボタンをクリックしてみてください。
初期化関数
以下の例を見てください。 ここでは、状態の初期値を取得するために getItems 関数を呼び出しています。
import React from "react";
import { useState } from "react";
function ListItems() {
const getItems = () => {
console.log(`%c getItems called`, "color:hotpink");
return Array(50).fill(0);
};
const [items, setItems] = useState(getItems());
return (
<div className="card">
<ul> {items.map((item, index) =>
( <li key={index}>Item {index + 1} </li>))}
</ul> <button onClick={() => setItems([...items, 0])}>Add Item</button> </div> );
}
export default ListItems;
この関数は、サイズ 50 の配列を作成し、配列をゼロで埋めます。 下の画像を参照してください。
これらの項目が画面に表示されます。
すべてがうまくいっているように見えますが、ここで問題が発生しました。
セットアップボタンをクリックすると、セットアップが開始されます アイテムを追加 ボタン (アイテムのリストの後ろにあります) をクリックして、新しいアイテムをリストに追加します。 ログを観察します。
ここに問題がありますか?
アイテムを追加するたびに、ログ「getItems called」がコンソールに追加されます。 これは、この関数がレンダリングごとに呼び出されることを意味します。
useState は、最初のレンダリング後に渡された初期値を無視しますが、ここでは初期値がまだ再計算されていることに注意してください。 大きな配列を作成したり、重い計算を実行したりする場合、これはコストがかかる可能性があります。
渡すことでこの問題を解決できます アイテムの取得 _として イニシャライザ _ 関数。
では、コードに小さな変更を加えてみましょう。
const [items, setItems] = useState(getItems);
CodeSandbox のコンソール ウィンドウを参照してください。 「getItems called」ログは最初のレンダリングでのみ出力されることに注意してください。 後続のアイテムが追加されると、このログはありません。
XNUMX つの例に視覚的な違いはありませんが、パフォーマンスの点では異なります。
初期状態の関数が必要な場合は、常に関数を渡すか、別の関数内で関数を呼び出してください。 関数を直接呼び出さないでください。
✅ const [items, setItems] = useState(getItems);
✅ const [items, setItems] = useState(() => getItems());
❌ const [items, setItems] = useState(getItems());
useState フックをいくつ持つことができますか
コンポーネント内には、必要な数の useState フックを含めることができます。
コードサンドボックスを見る
以下のコンポーネントには、ユーザー名、パスワード、keepMeSignedIn という XNUMX つの異なる状態があります。
ユーザー名、keepMeSignedIn の値を更新してみてください。 ログインボタンをクリックすると、更新された状態がコンソールに記録されます。
特徴
- useState は、再レンダリングをトリガーし、再レンダリング間で状態を保持するメカニズムを提供します。
- 次の必要がある場合は、アップデータ機能を使用します。
- 前の状態に基づいて次の状態を計算します。
- 次のレンダリングの前に、状態に対して複数の更新を実行します。
- 関数から初期状態を取得する場合は、初期化関数の構文を使用します。
- コンポーネント内に複数の useState フックが存在する場合があります。
この投稿が気に入りましたか? 他の人と共有します。
個人ブログ用に書いたものですが、 https://gauravsen.com/use-state-hook