HTML5テクニカルノート
Create React App フックによる状態管理 01: 基本のuseStateを使う
- ID: FN2011001
- Technique: ECMAScript 2015
- Library: React 17.0.1
このチュートリアルシリーズでは、Reactにおける状態(state)の管理について、おもにフックを使った解説をしてゆきます。React 16.8からは、関数コンポーネントでも、状態が保てるようになりました。そのための機能がフック(hook)です(「フックの導入」参照)。本稿では、もっとも基本となるステートフックuseState
で、関数コンポーネントに状態を備えます。サンプルに採り上げるのは簡単なカウンターのアプリケーションです(図001)。
図001■リセット・減算・加算の機能を備えたカウンター
01 Reactアプリケーションのひな形をつくる
アプリケーションのひな形は、Create React Appでつくります。まずは、Create React Appのインストールです。予めNode.js(npm)が入っていなければなりません。コマンドラインツールからnpmで、npx create-react-app
コマンドにつづけてアプリケーション名(react-state-management
としました)を入力してください。その名前でフォルダとアプリケーションのひな形がつくられます。
npx create-react-app react-tic-tac-toe
ひな形がつくられたら、アプリケーションのディレクトリに移って、yarn start
とコマンドを打てば、ローカルサーバーでひな形のページが開きます(図002)。ローカルサーバーを閉じたいときは[control]/[Ctrl] + [C]、再開には改めてyarn start
を入力してください。
cd react-tic-tac-toe yarn start
図002■ひな形のReactアプリケーションのページ
02 素朴なuseStateの使い方
useState
を使うと、関数コンポーネントに状態を備えることができます。フック(hook)というのは、Reactの機能をつなぐ(hook into)関数です。useState
は、関数コンポーネントにReactの状態(state)をつなぎ止めます。戻り値は、ふたつの要素を収めた配列です。第1要素がstate
変数で状態を保ち、第2要素の関数によりその値を書き替えます。配列の分割代入を使って変数(const
)に取り出すのが便利でしょう。useState
の引数には、状態の初期値を渡してください。なお、フックはReactの関数内からしか呼び出せません。
import React, { useState } from 'react'; function 関数コンポーネント() { const [state変数, set関数] = useState(初期値); }
カウンターのコンポーネント(src/CounterDisplay.js
)とアプリケーションモジュール(src/App.js
)の記述は、以下のコード001にまとめました。他のファイルのコードや動きは、CodeSandboxに掲げたサンプル001でお確かめください。useState
フックの構文は、つぎの表001のとおりです。設定関数(setCount()
)の引数は、関数型で更新しました(コード001のモジュールsrc/CounterDisplay.js
に定めたコンポーネントCounterDisplay()
参照)。
表001■useStateフック
useState |
|
引数 |
state変数に与える初期値。
|
戻り値 | state 変数と設定関数を要素に収めた配列。
設定関数には、値でなく関数を渡すこともできる。その場合、引数は直近の
|
コード001■useStateをカウンターのコンポーネントで使う
src/CounterDisplay.js
import React, { useState } from 'react';
const CounterDisplay = ({ initialCount = 0 }) => {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
</>
);
};
export default CounterDisplay;
import React from 'react';
import CounterDisplay from './CounterDisplay';
import './App.css';
function App() {
return (
<div className="App">
<CounterDisplay />
</div>
);
}
export default App;
サンプル001■React state management 01-01: Simple useState
状態変数を更新するとき状態変数はできるだけ参照しない方が望ましい
前掲コード001の状態設定関数(setCount
)は、関数型の更新で状態変数(count
)値を書き替えました。つぎのように状態変数を参照して、設定関数の引数に渡してはいけないのでしょうか。
{/* <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button> */} <button onClick={() => setCount(count - 1)}>-</button>
今回の例では、問題は生じないでしょう。けれど、複数の状態変数を参照して処理する場合など、更新値を正しく得られないことがあります。関数型の更新であれば、状態変数を直に参照したことにはなりません。可能な場合は、できるだけ状態変数を直接得るのは避けることが望ましいでしょう(「Reactのstate更新におけるバッチ処理と『関数型のstate更新』がなぜ必要なのか?について」)。
03 状態は親コンポーネントに管理させる
前項のコード001で、カウンターは動きました。でも、動けいただけでは、ユーザーインタフェースにはなりません。その設定を他のコンポーネントが参照できなければならないでしょう。今回の例では、状態を親のアプリケーションモジュールにもたせれば、その下層の子コンポーネントに値を与えられます。そこで、カウンターのコンポーネント(CounterDisplay
)の状態とロジックは、つぎのようにほぼそのままアプリケーションのモジュールsrc/App.js
に移してください。カウンターのコンポーネントには、プロパティとして加えればよいのです。
src/App.js// import React from 'react'; import React, { useState } from 'react'; const initialCount = 0; function App() { const [count, setCount] = useState(initialCount); const reset = () => setCount(initialCount); const decrement = () => setCount((prevCount) => prevCount - 1); const increment = () => setCount((prevCount) => prevCount + 1); return ( <div className="App"> {/* <CounterDisplay /> */} <CounterDisplay count={count} reset={reset} decrement={decrement} increment={increment} /> </div> ); }
カウンターのコンポーネント(
src/CounterDisplay.jssrc/CounterDisplay.js
)は、受け取ったプロパティをJSXコードに差し込みます。// import React, { useState } from 'react'; import React from 'react'; // const CounterDisplay = ({ initialCount = 0 }) => { const CounterDisplay = ({ count, reset, decrement, increment }) => { // const [count, setCount] = useState(initialCount); /* ...[中略]... */ return ( <> Count: {count} {/* <button onClick={() => setCount(initialCount)}>Reset</button> */} <button onClick={reset}>Reset</button> {/* <button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button> */} <button onClick={decrement}>-</button> {/* <button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button> */} <button onClick={increment}>+</button> </> ); };
これでカウンターの動きは変わらず、状態とロジックをアプリケーションモジュールに移せました。動作とコードを確かめるためのサンプル001をCodeSandboxに公開します。
コード002■useStateとロジックを親コンポートに移した
src/App.js
import React, { useState } from 'react';
import CounterDisplay from './CounterDisplay';
import './App.css';
const initialCount = 0;
function App() {
const [count, setCount] = useState(initialCount);
const reset = () => setCount(initialCount);
const decrement = () => setCount((prevCount) => prevCount - 1);
const increment = () => setCount((prevCount) => prevCount + 1);
return (
<div className="App">
<CounterDisplay
count={count}
reset={reset}
decrement={decrement}
increment={increment}
/>
</div>
);
}
export default App;
import React from 'react';
const CounterDisplay = ({ count, reset, decrement, increment }) => {
return (
<>
Count: {count}
<button onClick={reset}>Reset</button>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</>
);
};
export default CounterDisplay;
サンプル002■React state management 01-02: useState in App module
作成者: 野中文雄
作成日: 2020年11月03日
Copyright © 2001-2020 Fumio Nonaka. All rights reserved.