前回記事【React】入門してみたまとめ ✍🏻 の続きです。
useStateとは
変更した値を保持しつつ再レンダリングを可能にする仕組みがuseState()
。
関数コンポーネントでstateを管理(stateの保持と更新)するためのReactフックです。
state(状態)とは、コンポーネント毎に保持・管理される値。
useStateを使う際の注意点
- コンポーネント、カスタムフックの中で呼び出す
- if文やfor文の中で呼び出さない
- 値の更新と再レンダリングは予約(非同期)される
- 前回のstate値を使用する場合は更新用関数の引数に関数を渡す
- オブジェクト型のstateを更新する際は新しいオブジェクトを作成する
- stateの値をコンポーネント毎に独立して管理される
- 一度消滅したコンポーネントのstateの値はリセットされる
- stateをpropsとして渡すことで子コンポーネントで利用できる
- コンポーネントの位置によってはstateが維持される
useStateの使い方
import { useState } from "react";
const Example = () => {
const [val, setVal] = useState("testState");
return (
<>
<input
type="text"
onChange={(e) => {
setVal(e.target.value);
}}
/>
: {val}
</>
);
};
export default Example;
上記関数コンポーネントを実行すると下記のような表示になり、初期値「testState」がinputに入力した値に応じて即時的にレンダリングされます。
コード解説
import { useState } from "react";
useStateを使用するためにはreactライブラリからimportする必要があります。
const [val, setVal] = useState("testState");
こちらでuseState()
を実行しています。
useState()
の引数が値の初期値になります。
useState()
を実行して、返ってくる値は、配列です。
どんな配列かというと、0番目に参照用の値、1番目に更新用の関数の配列です。
その返ってきた値を分割代入しています。
試しにconsole出力してみると配列が返ってきていることがわかるかと思います🧑🚀
console.log(useState("初期値"));
<input
type="text"
onChange={(e) => {
setVal(e.target.value);
}}
/>
: {val}
そしてこちらのjsx部分では、onChange
でinputに更新があるたびに、setVal()
、つまり分割代入した更新用の関数が実行されています。
setVal()
の引数には新しい値を渡します。
今回はe.target.value
でinputに入力したvalueを渡しています。
そしてこのsetVal()
は実行されると関数コンポーネントの再実行、つまり画面の再レンダリングが発生します🌐
: {val}
で初期値が表示されており、setVal(e.target.value)
によってval
の値がe.target.value
に更新され、再レンダリングされることによって表示が変わっているという流れです。
オブジェクト型の値をステートしたい
オブジェクト型の値をステートしたい場合においても基本的には同じ使い方です。
import { useState } from "react";
const Example = () => {
const sampleObj = { name: "Sato", age: 26 };
const [obj, setObj] = useState(sampleObj);
const ChangeName = (e) => {
setObj({
name: e.target.value,
age: obj.age,
});
};
const ChangeAge = (e) => {
// setObj({
// name: obj.name,
// age: e.target.value,
// });
// ↑をスプレッド演算子で書いた場合。これで、objオブジェクトがオブジェクトとして展開されることになり、更新したいプロパティだけ「,」に続けて書く。
setObj({
...obj,
age: e.target.value,
});
};
const reset = () => {
setObj({
name: "",
age: "",
});
};
return (
<>
<p>Name: {obj.name}</p>
<p>Age: {obj.age}</p>
<input type="text" value={obj.name} onChange={ChangeName} />
<input type="number" value={obj.age} onChange={ChangeAge} />
<button onClick={reset}>リセット</button>
</>
);
};
export default Example;
ChangeAgeなどのsetObj()
で新しいstateの値となるオブジェクトを記述しているのがわかります。
setObj({
name: obj.name,
age: e.target.value,
});
オブジェクトをステートしたい場合においても、初期値と同じ形(今回はオブジェクトなのでオブジェクト)で新しい更新用の値を引数として渡せばいいだけです。
今回のサンプルではプロパティがname,ageの二つしかないですが、もしプロパティが10個あったらいちいち全てを記述するのは面倒です🤦
そこでスプレッド演算子を記述します。下記のコードで上記コードと同じ動作をします。
setObj({
...obj,
age: e.target.value,
});
...obj
のスプレッド演算子部分で、objオブジェクトが展開され、「,」に続けて値を更新したいプロパティのみを記述します。
配列型の値をステートしたい
配列の値は連結され描画されます。
import { useState } from "react";
const Example = () => {
const numArray = [1, 2, 3, 4, 5];
const [nums, setNums] = useState(numArray);
const shuffle = () => {
const newNums = [...nums]; // スプレッド演算子で既存のnums配列を展開し新しい配列を作成
const value = newNums.pop();
newNums.unshift(value);
setNums(newNums);
};
return (
<>
{nums}
<button onClick={shuffle}>シャッフル</button>
</>
);
};
export default Example;
const newNums = [...nums];
部分でスプレッド演算子で既存の値を展開し新しい値を作成しています。
このnewNums
をコネコネして、新しい値としてsetNums
に渡しています。
newNums
のような新しい変数を作成しなくてもnums
をコネコネすればいいような気がしますが、それでは動作しなかったです..。
更新用の新しい値は必ず初期のオブジェクトと異なる必要があるようです。
これはオブジェクト型をステートする場合においても同様です。