trifle

技術メモ

簡単な例で考える React Hooks のよさ

最近安定版にも導入された React Hooks が自分の中で面白いので, とても簡単な例で試してみたよという話をします.

例1: フォーム

class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'ここにポエムを書いてね'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('お前のポエムはこれだ: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          ポエム記入欄:
          <br/>
          <textarea value={this.state.value} onChange={this.handleChange} />
          <br/>
        </label>
        <input type="submit" value="送信" />
      </form>
    );
  }
}

export default Home;

これはフォームになっていて, 書いた内容がそのまま表示されます.

f:id:HelloRusk:20190223232720p:plain

こんな感じ. 実は公式ドキュメントの例をほぼそのまま使っているだけですね



これを React Hooks の useState で書き直してみるとこのくらいに省力化できます.

export default () => {
  const [value, setValue] = useState('ここにポエムを書いてね');

  const handleChange = event => {
    setValue(event.target.value);
  }

  const handleSubmit = event => {
    alert('お前のポエムはこれだ: ' + value);
    event.preventDefault();
  }

  return (
    <form onSubmit={() => handleSubmit(event)}>
      <label>
        ポエム記入欄:
        <br/>
        <textarea value={value} onChange={() => handleChange(event)} />
        <br/>
      </label>
      <input type="submit" value="送信" />
    </form>
  );
}

どうですか?綺麗じゃないですか?
value が状態としての this.state.value から, ただのローカル変数に落ちたことで, めちゃくちゃすっきり書けていますよね.
注意としては, React Hooks を使う場合は(すなわち Classではなく関数で書く場合は)Class の時のように this.handleSubmit = this.handleSubmit.bind(this); というような初期化ができず, 代わりに onSubmit のプロパティの部分で () => handleSubmit(event) とアロー関数を渡します.
これはどういうことかを考えると, https://uhyohyo.net/javascript/11_2.html にあるように, bind の戻り値は新しい関数なんですね. Class を使う場合では onSubmit={this.handleSubmit} だけでは関数は更新されず, コンストラクタの部分で bind で呼び出すことで初めて更新されるんですね.
一方, アロー関数で書けば矢印の先は評価されるので, ここで既に関数の更新が行われます. というわけで, Class を使わない functional component の例では, 必ずこういう関数自体を更新したいケースではアロー関数になっているのだと理解しています.



例2: 時計

React の中での大切な概念として Lifecycle というものがあります. といっても自分も正確に理解しているわけではありませんが...

qiita.com

がいつ見ても分かりやすいなと思います.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

export default Clock;

さてこれは現在の時刻をリアルタイムで表示する例です(これも公式ドキュメントの例から). componentDidMount で1秒後に時刻を更新して, componentWillUnmount でそのリソースを解放しています.
このような「状態の変化に応じて何かをする」ときに役立つ React Hooks が useEffect で, これを使うと

export default () => {
  const [date, setDate] = useState(new Date());

  const tick = () => {
    setDate(new Date());
  }

  useEffect(() => {
    const timerID = setInterval(
      () => tick(), 
      1000
    );
    return () => {
      clearInterval(timerID);
    };
  })

  return (
    <div>
      <h2>It is {date.toLocaleTimeString()}.</h2>
    </div>
  )
}

これまた非常に簡潔になりました.
useEffect は優れもので, 公式ドキュメントを読むと,

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

ということが分かります. Lifecycle を集約できるのです.



時代はどんどん functional component に向かっていくのでは

今まで見てきた内容から分かると思うのですが, React の公式ドキュメントは現状かなり Class で書かれています.
React に触れる人がまず目を通すであろう React Tutorial も, ただ DOM を render するだけのパーツでさえも Class で書かれているのが分かります. これはちょっととっつきづらいでしょう.
自分が初めて React に触れた時も Class がたくさん出てきたのに驚いた記憶があります. 使い方が分かっていくうちに Class を使わない functional component でも事足りる場合が多いことに気づいていきました. そこに追い打ちをかけるのがこの React Hooks の登場だと思います.

一方で, Facebook 社は Class を完全に捨て去るわけでもないようです. https://reactjs.org/docs/hooks-intro.html#gradual-adoption-strategy を読んでみてください.

We intend for Hooks to cover all existing use cases for classes, but we will keep supporting class components for the foreseeable future. At Facebook, we have tens of thousands of components written as classes, and we have absolutely no plans to rewrite them. Instead, we are starting to use Hooks in the new code side by side with classes.