React Hooksの一つ stateを扱うuseState
目次
はじめに
こんにちは。Kennyです。今回はReact.jsのHooksであるuseStateを解説していきたいと思います。useStateはfunctional componentで使用できる機能でstateを使えるようにします。他にもuseEffectやuseCallback,useReducer, useMemoなどありますが今回はuseStateについてやっていきましょう。
前提
開発環境
macOS Catalina
MacBook Pro (15inch, 2019)
npm version 6.14.4
nodebrew use v14.0.0(node)
yarn 1.22.4
React 16.13.1
React-Dom 16.13.1
useStateを使うにはReactのversionが16.8以上
functional componentでstateを扱えるようになる機能なのですが、新しい機能なのでversionをお確かめください。16.8以上でない場合はインストールが必須になります。react.jsのアプリケーションを作成するには予めnodeのinstallが必要です。
useStateを使ってみよう
Summary(概要)
useStateにはいくつかの決まりや振る舞いがあります。それらの大筋を解説していきたいと思います。
1.useStateはfunctional componentでstateを使用するHooksです。
2.class componentではstateはオブジェクトですが、useStateでは必ずオブジェクトである必要はありません。
3. 最初のエレメントは現在の値で、二つ目のエレメントはセッターfunctionの値です。
useStateのインポート
React HooksのuseStateを使用するのにプラグインやモジュールを使用する必要はありません。使用するにはversionが16.8以上である必要があります。
import React, { useState } from 'react'
functional component内でuseStateを使用する
App関数コンポーネント内でカウンターを作成する場合で考えていきます。使用する状態はcount, そのカウントを更新する関数はsetCountとし、カウントの初期値は0としてみましょう。
const [count, setCount] = useState(0)
上記のcountは一つ目のエレメントは現在の値で、二つ目がセッター関数の値が入ってきます。useState(0)とは初期値の0を意味します。最初の値は0でカウントアップしていく仕様です。では全体のコードを記述します。
import React, { useState } from 'react'
const App = () => {
const [count, setCount] = useState(0)
const increment = () => {
setCount(count + 1)
}
const decrement = () => {
setCount(count - 1)
}
const divideByTwo = () => {
setCount(previousCount => {
return previousCount % 2 === 0 ? previousCount / 2: previousCount
})
}
const resetCount = () => {
setCount(0)
}
return (
<div>
<p>count: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={divideByTwo}>2でわる</button>
<button onClick={resetCount}>Reset</button>
</div>
)
}
export default App;
上記のカウンターの解説
上記のカウンターはプラス1するボタン、マイナス1するボタン、2の倍数の時は2で割るボタン、リセットして0に戻すボタンがあり、その値を表示しています。
プラス1する関数とクリックイベント
const increment = () => {
setCount(count + 1)
}
//jsxのボタン要素
<button onClick={increment}>+1</button>
プラス1する関数とuseStateの関係をみていきます。setCount関数の引数に行たい処理を記述し、increment関数としてクリックイベントを定めます。const を必要としますがclass componentでの関数ではconstをつかいません。functional componentでは関数にconstを使用するといった違いがあります。ちなみにclass componentで同じ関数を定義する時は次のようになります。
increment = () => {
this.setState({
count: this.state.count + 1
})
}
//jsx
<button onClick={this.increment}>+1</button>
setStateを使用する時、thisを多用します。funcional componentではthisを使わずにかけることが大きな違いでもあります。
マイナス1する関数とクリックイベント
マイナス1する関数には大きな違いがありません。ボタンにインナー関数が定められていますが関数名だけで良い利点があります。
const decrement = () => {
setCount(count - 1)
}
//jsxのボタン要素
<button onClick={decrement}>-1</button>
2の倍数の時2で割る関数とクリックイベント
useStateのsetter functionは前回のstateの値を取得することができます。更新された値はなんの値が元になった値なのかが重要であり、イメージを正確に捕らえたい場合に有効な手段です。それにsetCount内では引数に関数処理することができることにも注目です。こちらの処理は三項演算子を用いて条件分岐しています。2で割った値に余りがない場合は2で割る処理を実行します。
const divideByTwo = () => {
setCount(previousCount => {
return previousCount % 2 === 0 ? previousCount / 2: previousCount
})
}
//jsxのボタン要素
<button onClick={divideByTwo}>2でわる</button>
もう一つ例をあげたいと思います。
const incrementThree = () => {
for (let i=0; i<3; i++) {
setCount(previousCount => previousCount + 1)
}
}
//jsxのボタン要素
<button onClick={incrementThree}>Increment3</button>
loop処理にsetCountを使用した場合です。0,1,2と三回同じ処理を行った場合、前回の値を使う処理と単に1をプラスする処理と比べて結果はどうなると思いますか。では結果を見比べてみましょう。
上記の場合の結果は3,6,9と3の倍数で増えていきます。ではpreviouseCountを使用しない場合ではどうでしょう。
const incrementThree = () => {
for (let i=0; i<3; i++) {
setCount(count + 1)
}
}
//jsxのボタン要素
<button onClick={incrementThree}>Increment3</button>
上記の記述ではpreviousCountを利用しない単に1をプラス処理となって、1,2,3,4と1づつ増えていきます。これでは結果が変わってしまいます。今回の場合3の倍数にするためにより正確な記述にしたいなら前回の値を用いる方が良いでしょう。ではこれまでのincrement関数やdecrement関数も書き換えてみましょう。
const increment = () => {
setCount(previousCount => previousCount + 1)
}
const decrement = () => {
setCount(previousCount => previousCount - 1)
}
リセットする関数とクリックイベント
最後にリセットする関数ですね。初期値の0を渡せばリセットすることができます。
const resetCount = () => {
setCount(0)
}
//jsxのボタン要素
<button onClick={resetCount}>リセット</button>
stateにオブジェクトを扱ってみよう
ではこここからはオブジェクトを扱ってみましょう。input要素とformを使ったfirstNameとlastNameを表示する機能を作るにはどうしたらよいでしょう。次のように記述してみてください。
const [name, setName] = useState({ firstName: '', lastName: ''})
初期値の値をオブジェクトで渡します。firstName,lastNameどちらも空の値を用意しておきます。こうするとinputタグのvalueの値はどうなるでしょう。
<input
type="text"
value={name.firstName}
/>
valueの値をname.firstNameとすることでアクセスすることができます。ではlastNameもやってみましょう。
<input
type="text"
value={name.lastName}
/>
表示させるためには
<h2>first name is {name.firstName}</h2>
<h2>first name is {name.lastName}</h2>
となります。ではチェンジイベントでデータをsetNameに渡してみましょう。
<input
type="text"
value={name.firstName}
onChange={(e) => setName({firstName: e.target.value})}
/>
<input
type="text"
value={name.lastName}
onChange={e => setName({lastName: e.target.value})}
/>
このようにするとわかることがあります。setNameのに渡ってくるデータはfirstNameとlastNameとありますが二つ目の入力が始まると消えてしまいます。何度か試してみて欲しいと思いますが,実はマージされてしまっているようです。値を更新してしまっているので正しくどちらの値も含めるためには次のように変更します。
return (
<div>
<form>
<input
type="text"
value={name.firstName}
onChange={(e) => setName({...name, firstName: e.target.value})}/>
<input
type="text"
value={name.lastName}
onChange={e => setName({...name, lastName: e.target.value})}/>
<h2>first name is {name.firstName}</h2>
<h2>first name is {name.lastName}</h2>
</form>
</div>
)
スプレッド構文でnameを展開し、新たにfirstNameやlastNameのオブジェクトを含めることで正しく更新することができます。
stateの値を決めておく
もちろん初期値に値を代入しておくことができます。
例えばこのようにstateの値をcarStatesオブジェクトとして予め決めておくことも出来ます。
defaultPropsでstateの値を決めておく
defaultPropsを使えばpropsとしてアクセスすることができます。すっきりした見た目で良いかもしれません。
useStateを使ってstateの更新をしよう
priceの値を変えていきます。初期値のうちpriceは変動するので数字に変更します。
import React, { useState } from 'react'
const App = (props) => {
// const carStates = {
// name: 'ランクル',
// price: '100,000'
// }
const [name, setName] = useState(props.name)
const [price, setPrice] = useState(props.price)
return (
<>
<div>
{name}の価格は{price}円です
</div>
<button onClick={() => setPrice(prevPrice => prevPrice + 1)}>+1click</button>
<button onClick={() => setPrice(prevPrice => prevPrice - 1)}>-1click</button>
<button onClick={() => setPrice(props.price)}>reset</button>
</>
)
}
App.defaultProps = {
name: 'ランクル',
price: 100000
}
export default App;
nameの値を変えていきます。nameの初期値も空に変えておきます。
import React, { useState } from 'react'
const App = (props) => {
// const carStates = {
// name: 'ランクル',
// price: '100,000'
// }
const [name, setName] = useState(props.name)
const [price, setPrice] = useState(props.price)
return (
<>
<div>
{name}の価格は{price}円です
</div>
<button onClick={() => setPrice(price + 1)}>+1click</button>
<button onClick={() => setPrice(price - 1)}>-1click</button>
<button onClick={() => setPrice(props.price)}>reset</button>
<input value={name} onChange={e => setName(e.target.value)}/>
</>
)
}
App.defaultProps = {
name: '',
price: 100000
}
export default App;
初期値を代入することでresetしよう
文章
import React, { useState } from 'react'
const App = (props) => {
// const carStates = {
// name: 'ランクル',
// price: '100,000'
// }
const [name, setName] = useState(props.name)
const [price, setPrice] = useState(props.price)
const reset = () => {
setPrice(props.price)
setName(props.name)
}
return (
<>
<div>
{name}の価格は{price}円です
</div>
<button onClick={() => setPrice(price + 1)}>+1click</button>
<button onClick={() => setPrice(price - 1)}>-1click</button>
<button onClick={() => setPrice(props.price)}>reset</button>
<button onClick={reset}>reset</button>
<input value={name} onChange={e => setName(e.target.value)}/>
</>
)
}
App.defaultProps = {
name: '',
price: 100000
}
export default App;
nameもpriceもオブジェクトだからまるっとオブジェクトを渡してみよう
useStateの一つ目のエレメントにstateとしてdefaultPropsを渡します。そうすると初期値はpropsとなり、setStateの処理で正しく更新させるためにstateを展開してマージします。
import React, { useState } from 'react'
const App = props => {
// オブジェクトを渡す
const [state, setState] = useState(props)
return (
<>
<div>
{state.name}の価格は{state.price}円です
</div>
<button onClick={() => setState({ ...state, price: state.price + 1})}>+1click</button>
<button onClick={() => setState({ ...state, price: state.price - 1})}>-1click</button>
<button onClick={() => setState(props)}>reset</button>
<input value={state.name} onChange={e => setState({...state, name: e.target.value})}/>
</>
)
}
App.defaultProps = {
name: '',
price: 100000
}
export default App;
state.nameやstate.priceをname,priceに変えたいなら分割代入が便利
わざわざstate.nameなどとアクセスするのが面倒であれば次のように分割代入をするとすっきりします。
const [state, setState] = useState(props)
// 分割代入
const {name, price} = state
では全コードです。
import React, { useState } from 'react'
const App = props => {
// オブジェクトを渡す
const [state, setState] = useState(props)
// 分割代入
const {name, price} = state
return (
<>
<div>
{name}の価格は{price}円です
</div>
<button onClick={() => setState({ ...state, price: price + 1})}>+1click</button>
<button onClick={() => setState({ ...state, price: price - 1})}>-1click</button>
<button onClick={() => setState(props)}>reset</button>
<input value={name} onChange={e => setState({...state, name: e.target.value})}/>
</>
)
}
App.defaultProps = {
name: '',
price: 100000
}
export default App;
まとめ
いかがだったでしょうか。今回はReact.jsの React Hooksについて解説しました。クラスコンポーネントで使用していたstateを関数コンポーネントで使うことができなかったんですが、React Hooksの登場によりすっきりしたコンポーネントでの記述もできるようになりましたね。useStateだけでなくHooksにはいろいろあるので試してスキルを伸ばしていきましょう。