初心者でもわかるReact Hook のuseStateを使い方
Reactの入門者がReact Hookを学び始めた時に最初に理解しなければならないのがuseState Hookです。useStateを使うとFunctionalコンポーネント(関数コンポーネント)内で状態管理を行うことができます。初めて状態管理という名前を聞く人にとってイメージが湧きにくい単語ですが要はコンポーネントの中で変数を扱うことができ変数に保存した値を保持することができる機能です。例えば入力フォームのinput要素に入力した文字をコンポーネント内で保持したい場合にuseState Hookを利用することができます。
useState Hookはコンポーネントの中で変数の値を保持、更新ができることからReactの中も最も使用頻度の高い機能なので簡単なサンプルを使って動作確認を行なっていきます。
目次
シンプルな例を通してuseStateの理解
useStateの設定
コンポーネント内でuseStateを利用するためには、useStateをimportする必要があります。
import { useState } from 'react';
コンポーネント内で状態管理として利用したい変数をuseStateを使って宣言します。初めて使用する人であれば最初は書式に違和感のある方も多いもしれませんが使えばすぐに慣れるものです。まず最初にuseStateの書式をしっかりと覚えてください。
importしたuseState Hookの引数に初期値を設定します。useStateの戻り値は配列で一つ目の要素には状態(変数)が戻され、二つ目の変数には状態を更新するための関数が戻されます。下記の例ではuseState関数から戻されるcountが変数となりsetCount関数を利用してcountの値を更新することができます。countの初期値は0です。
const [count, setCount] = useState(0);
上記の設定を行うだけでコンポーネント内のJSX関数に{ count }を追加するとcountの現在値を表示することができます。初期値は0に設定し、そこから何も変更を行っていないためブラウザ上には初期値である0が表示されます。初期値の値を変更するとブラウザ上に表示される値も変わります。
import { useState } from 'react';
function Count() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Counter</h1>
<h2>カウント: { count }</h2>
</div>
);
}
export default Count;
countの値の更新
先程setCount関数でcountの値を更新できると説明しました。どのようにcountの値を更新するのか実際にコードを使って確認していきます。
クリックしたらcountの値が更新できるようにボタンを追加しonClickイベントを追加します。onClickの中でsetCount関数を利用してcountの値を更新します。
<button onClick={() => setCount(count + 1)}>+</button>
countを1増やすだけではなく-1に減らすボタンも追加します。
import { useState } from 'react';
function Count() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Counter</h1>
<h2>カウント: { count }</h2>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
export default Count;
+ボタンをクリックすると1増え、-ボタンをクリックすると1減るカウンターが作成できました。ボタンを押すとコンポーネントの内で行ったカウントの増減が即座にブラウザ上にも反映されます。
onClickで実行している関数を外側に定義しても動作は変わりません。このようにuseStateを利用するとコンポーネント内で値を保持したり更新したりすることができます。
import { useState } from 'react';
function Count() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return (
<div>
<h1>Counter</h1>
<h2>カウント: { count }</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
export default Count;
更新は非同期
increment関数を使うことでcountの数を1増やすことは可能です。しかしincrement関数の中で増やしたcountで何か処理をしようとしても更新は非同期なため更新した後のcountの値を取得することはできません。
const increment = () => {
setCount(count + 1);
console.log(count);
};
+ボタンを押すとコンソールには1増えたcountが表示されると思いますが実際にはcountは即座に更新されていないためボタンを押しても数は増えません。
下記のように+ボタンを押すとブラウザ上のカウントは1増えますがコンソールログのcountは0のままです。
countがsetCountで再描写が行われた後にはcountは更新されていることが確認できます。再描写されたことがわかるようにコードを追加します。
import { useState } from 'react';
function Count() {
const [count, setCount] = useState(0);
console.log('再描写');
console.log(count);
const increment = () => {
setCount(count + 1);
console.log(count);
};
const decrement = () => setCount(count - 1);
return (
<div>
<h1>Counter</h1>
<h2>カウント: {count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
export default Count;
ページを開くと再描写ではありませんが初めてコンポーネントが実行されるのでコンソールには再描写と0が表示されます。
ボタンをクリックします。incremant関数内ではcountは更新されていませんが再描写した後はcountが更新されていることが確認できます。
useStateでbooleanを使う
状態管理を行う変数の値にbooleanを使用することができます。booleanのtrueとfalseを利用することでコンポーネントの表示・非表示を切り替える方法はReactアプリケーションのさまざまな場所で利用されています。
ONボタンとOFFボタンで電源が切り替わるPowerコンポーネントを作成します。
import { useState } from 'react';
function Power() {
const [power, setPower] = useState(false);
return (
<div>
<h1>電源 { power ? 'ON' : 'OFF'} </h1>
<button onClick={() => setPower(true)}>ON</button>
<button onClick={() => setPower(false)}>OFF</button>
</div>
);
}
export default Power;
useStateで初期値をtrueまたはfalseに設定することができます。上記ではuseStateでfalseに設定しているのでpowerの初期値はfalseになります。
ONボタンとOFFボタンを用意しonClickイベントを使ってpowerの値をtrueまたはfalseに変更できるように設定します。
ブラウザで確認すると下記のように表示され、ONボタンを押すと電源ONと表示され、OFFボタンを押すと電源OFFと表示されます。
前の値を利用して切り替える
ONとOFFを切り替えるのに2つのボタンを用意していましたが、useStateでは現在の値を元にして新しい値を設定することができます。現在の値を利用したい場合にはsetPower関数の中で関数を利用します。prevStateがボタンをクリックする前の値でボタンをクリックするとprevStateの値で別の値に変わります。つまり現在の値がtrueであればfalse、falseであればtrueに変わります。
<button onClick={() => setPower(prevState => !prevState)}>ON/OFF</button>
prevStateとしていますが、名前は任意なのでどのような名前でもつけることが可能です。
ボタンをクリックするたびにONとOFFが切り替わります。コンポーネントの表示・非表示を行うToggle機能もこのようにuseState Hookの値を利用して実装することができます。
useStateを利用してフォーム
コンポーネント内でのuseState Hookは入力フォームで利用する機会が多いのでシンプルなフォームを作成を通してさらにuseStateの理解を深めていきましょう。
これからuseState Hookの入力フォームでの利用方法を確認していきますが、公式の旧ドキュメントのuseStateのNoteの下記の画像に記載されている説明の意味がわからない場合はこれから行っていく説明が理解の助けになるかと思います。
メールアドレスとパスワードの入力項目を備えたフォームを作成します。emailとpasswordはuseStateを利用して入力の値を取得します。input要素からはイベントを利用して入力した値を取得しています。
import { useState } from 'react';
function App() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSend = (e) => {
e.preventDefault();
console.log(email, password);
};
return (
<div style={{ textAlign: 'center', marginTop: '2em' }}>
<h1>useStateでフォーム</h1>
<form onSubmit={handleSend}>
<div>
<label>
メールアドレス:
<input
name="email"
type="email"
onChange={(e) => setEmail(e.target.value)}
/>
</label>
</div>
<div>
<label>
パスワード:
<input
name="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default App;
ブラウザで確認するとメールアドレスとパスワードにの入力を行いSubmitボタンを押すと入力した情報が表示されます。
ここまでのuseState Hookの使い方が理解できていれば難しいところはないかと思います。
emailとpasswordを別々の変数にわけて設定を行うのではなくformオブジェクトをuseState Hookの初期値として設定します。先程までの方法ではemail、password以外に入力項目が増えてたら増えた分だけuseStateで新たに変数を定義する必要があります。しかしformオブジェクトを利用した場合は初期値の中のオブジェクトに入力項目のプロパティを追加する必要があります。
const [form, setForm] = useState({ email: '', password: '' });
ではオブジェクトを利用した場合はどのようにformオブジェクトの値を更新することができるのでしょうか?
input要素にはname属性を設定していたのでname属性を利用してどちらの入力項目が入力されたかを確認することができます。
[e.target.name]: e.target.value
formオブジェクトを更新したい場合には更新前のprevStateとspread operatorsを利用して下記のように行うことができます。
setForm((prevState) => {
return {
...prevState,
[e.target.name]: e.target.value,
};
});
formオブジェクトを利用すると全体のコードを下記のように変更となります。onChageイベントはname属性でどの項目が入力されたか判定できるため同じhandleChage関数を設定しています。
import { useState } from 'react';
function App() {
const handleSend = (e) => {
e.preventDefault();
console.log(form);
};
const [form, setForm] = useState({ email: '', password: '' });
const handleChange = (e) => {
setForm((prevState) => {
return {
...prevState,
[e.target.name]: e.target.value,
};
});
};
return (
<div style={{ textAlign: 'center', marginTop: '2em' }}>
<h1>useStateでフォーム</h1>
<form onSubmit={handleSend}>
<div>
<label>
メールアドレス:
<input
name="email"
type="email"
onChange={handleChange}
/>
</label>
</div>
<div>
<label>
パスワード:
<input
name="password"
type="password"
onChange={handleChange}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default App;
ブラウザで動作確認をしてもemailとpasswordで変数を別々に定義した時と結果は同じです。
setFormのコードについては下記のようによりシンプルに記述することができます。
setForm({
...form,
[e.target.name]: e.target.value,
});
またObject.assignを利用することもできます。
setForm(Object.assign(form, { [e.target.name]: e.target.value }));
ここまでの説明が理解できればuseStateを使いこなすことが可能です。
Reactのフォームについてもう少し詳しく知りたい場合は下記の記事が参考になります。