React Hooksでカウンターを実装してみる。

とりあえず実装すると以下のような形か。

import React from 'react';

const Counter = () => {
    const [count, setCount] = React.useState(0);

    const handleClick = React.useCallback((e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
      setCount(c => c + 1);
    }, []);

    return (
      <div>
        <p>Count: {count}</p>
        <input type="button" value="Up" handleClick={handleClick}>
      </div>
    );
};

export { Counter }

カウント処理をuseCallbackで無駄に生成しないようにしている。

このカウント処理を、ほかのコンポーネント、特に子のコンポーネントで実施したいことがある。 子コンポーネントをRichButtonとすると、こんな感じか。

import React from 'react';
import { RichButton } from './RichButton';

const Counter = () => {
    const [count, setCount] = React.useState(0);

    const handleClick = React.useCallback((e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
      setCount(c => c + 1);
    }, []);

    return (
      <div>
        <p>Count: {count}</p>
        <RichButton handleClick={handleClick}></RichButton>
      </div>
    );
};

export { Counter }

ただ、Reactは親コンポーネントが更新されると、子コンポーネントも更新されるらしい。

今回だと、Counter(親コンポーネント)で管理しているcountを更新するとCounterコンポーネント以下のコンポーネントすべてに更新がかかる。しかし、RichButtonなどは状態が変わっていない(そもそも状態を持っていない)ので別に更新する必要はない。

その場合は、React.memoで関数コンポーネントをラップする必要があるらしい。

import React from 'react';

interface Prop {
  handleClick: (event: React.MouseEvent<HTMLInputElement, MouseEvent>) => void;
}

const RichButton = React.memo((props: Prop) => {
    const { handleClick } = props;

    return <input type="button" value="Button" onClick={handleClick}></input>;
});

export { RichButton }

ただ、memoするのにもコストはかかるので、どちらが効果的かはちゃんと計測していく必要があるらしい。 React、なんとなくでも動いちゃうが、パフォーマンスを気にする場合は結構めんどくさい。