1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FizzBuzzボタンを作りながらReact Hooksを学ぶ 第4回

Last updated at Posted at 2019-08-03

FizzBuzz ボタンを作りながら React Hooks を学んでいきます。
目次はこちら

Hooks を使って関数コンポーネントに書き換える

前回まででクラスコンポーネントを使う方法で、FizzBuzz ボタンを作りました。
今回はこれを React Hooks を使って書き換えていきます。
ようやく Hooks が出てきます。

FizzBuzzrender() の中身をそのまま持ってきて関数コンポーネントに改造していきます。

FizzBuzz.tsx(修正前)
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}
      />
    );
  }
}

だいたいこんな↓感じです。
ここから必要な箇所を書き換えていきます。

FizzBuzz.tsx(未完成版)
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 を使います。

FizzBuzz.tsx(未完成版、状態管理を実装)
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 は引数に初期値を取り、状態を表す変数と状態を変更する関数を配列で返します。

② ボタンを押したときの処理を実装する

FizzBuzz.tsx(未完成版、コールバックを実装)
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 が描画されるたびに plus1clear の値が変わってしまいます。
そのため、plus1clear を props で渡している FizzBuzzView が再描画されてしまいます。
これを防ぐために Callback Hook を使います。

FizzBuzz.tsx(完成版)
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引数に指定します。
clearprops.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 を使って関数コンポーネント化しておきます。

App.tsx
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;

今回作成したソースコードはこちら
次回はマウント、アンマウントされたときの処理を実装します。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?