FizzBuzz ボタンを作りながら React Hooks を学んでいきます。
目次はこちら。
Hooks を使って関数コンポーネントに書き換える
前回まででクラスコンポーネントを使う方法で、FizzBuzz ボタンを作りました。
今回はこれを React Hooks を使って書き換えていきます。
ようやく Hooks が出てきます。
FizzBuzz
の render()
の中身をそのまま持ってきて関数コンポーネントに改造していきます。
class FizzBuzz extends React.PureComponent<Props, State> {
...
render() {
console.log('render');
const { count } = this.state;
const { plus1, clear } = this;
const message = fizzBuzz(count);
return (
<FizzBuzzView
count={count}
message={message}
plus1={plus1}
clear={clear}
/>
);
}
}
だいたいこんな↓感じです。
ここから必要な箇所を書き換えていきます。
const FizzBuzz: React.FC<Props> = React.memo((props: Props) => {
console.log('render');
// const { count } = this.state; // ① 状態管理
// const { plus1, clear } = this; // ② ボタンを押したときの処理
const message = fizzBuzz(count);
return (
<FizzBuzzView count={count} message={message} plus1={plus1} clear={clear} />
);
});
① 状態管理には State Hook を使う
クラスコンポーネントでは状態の管理に this.state
を使っていましたが、関数コンポーネントでは State Hook を使います。
import React, { useState } from 'react';
const FizzBuzz: React.FC<Props> = React.memo((props: Props) => {
console.log('render');
const [count, setCount] = useState(props.initialCount);
// const { plus1, clear } = this; // ② ボタンを押したときの処理
const message = fizzBuzz(count);
return (
<FizzBuzzView count={count} message={message} plus1={plus1} clear={clear} />
);
});
useState
は引数に初期値を取り、状態を表す変数と状態を変更する関数を配列で返します。
② ボタンを押したときの処理を実装する
const FizzBuzz: React.FC<Props> = React.memo((props: Props) => {
console.log('render');
const [count, setCount] = useState(props.initialCount);
const plus1 = () => setCount(count => count + 1);
const clear = () => setCount(props.initialCount);
const message = fizzBuzz(count);
return (
<FizzBuzzView count={count} message={message} plus1={plus1} clear={clear} />
);
});
ボタンを押したときの処理では、State Hook で取得した setCount
を使って count の値を変更します。
コールバックを useCallback
で包む
この実装では FizzBuzz
が描画されるたびに plus1
、clear
の値が変わってしまいます。
そのため、plus1
、clear
を props で渡している FizzBuzzView
が再描画されてしまいます。
これを防ぐために Callback Hook を使います。
import React, { useState, useCallback } from 'react';
import FizzBuzzView from './FizzBuzzView';
import { fizzBuzz } from './common';
type Props = {
// props で count の初期値を受け取る
initialCount: number;
};
const FizzBuzz: React.FC<Props> = React.memo((props: Props) => {
console.log('render');
const [count, setCount] = useState(props.initialCount);
const plus1 = useCallback(() => setCount(count => count + 1), []);
const clear = useCallback(() => setCount(props.initialCount), [
props.initialCount,
]);
const message = fizzBuzz(count);
return (
<FizzBuzzView count={count} message={message} plus1={plus1} clear={clear} />
);
});
export default FizzBuzz;
useCallBack
は第1引数で渡した関数をメモ化します。
ざっくり、一種のキャッシュと理解すれば良いと思います。メモリ上に関数を保持しておくことにより、再描画時に関数が再生成されず、最初に作成した関数が使い回されます。これにより FizzBuzzView
の再描画が抑制されます。
第2引数には、第1引数の関数が依存している値の配列を渡します。
plus1
は常に同じ関数を使用すれば良いので、依存している値はない、つまり []
を第2引数に指定します。
clear
は props.initialCount
が変われば別の関数を使う必要があります。
(正確ではないですが、
props.initialCount = 0
の場合は、() => setCount(0)
を使い、
props.initialCount = 100
の場合は、() => setCount(100)
を使うと考えれば理解しやすいです。)
そのため、[props.initialCount]
を第2引数に指定します。
useCallBack
の第2引数に関しては、create-react-app-ts.sh で作成したアプリならば、VS Code で保存するたびに適切なものが補完されますので、あまり気にする必要はありません。
(Create React App でアプリを作成すると、eslint-plugin-react-hooks がインストールされます。ここに修正のルールが書かれています。)
App も関数コンポーネント化する
ついでに App
も State Hook, Callback Hook を使って関数コンポーネント化しておきます。
import React, { useState, useCallback } from 'react';
import FizzBuzz from './FizzBuzz';
const App: React.FC = () => {
const [disable, setDisable] = useState(false);
const hide = useCallback(() => setDisable(true), []);
const show = useCallback(() => setDisable(false), []);
return (
<>
{disable ? (
<div>
<button type="button" onClick={show}>
show
</button>
</div>
) : (
<div>
<div>
<button type="button" onClick={hide}>
hide
</button>
</div>
<FizzBuzz initialCount={0} />
</div>
)}
</>
);
};
export default App;
今回作成したソースコードはこちら。
次回はマウント、アンマウントされたときの処理を実装します。