React Hooksを活用してクリーンなコードを目指す

Reactに関するHooks系の記事を最近書いてなかったので書いておこうと思う。

Componentとmoduleの分離

Reactに処理を書く時、以下のようなかたちで書くことが多いと思う。(自分もそのうちの一人だった)
Component.tsx

import React, { useState } from 'react';

const Component: React.FC = (): JSX.Element => {
  const [isOpen, setIsOpen] = useState<boolean>(false);
  const onClick = (): void => {
    setIsOpen(!isOpen);
  }
  return (
    <p onClick={() => onClick()}>Click here</p>
  );
}
export default Component;

ただ、せっかくのHooksがあるので活用しましょう。
モジュールとコンポーネントは分離してしまった方がクリーンなコンポーネントを維持できるのでロジック部分をCustom Hooksとして分けてしまいます

Component.tsx

import React, { useState } from 'react';

const useLogic = () => {
  const [isOpen, setIsOpen] = useState<boolean>(false);
  const onClick = (): void => {
    setIsOpen(!isOpen);
  }
  return {
    isOpen,
    setIsOpen,
    onClick
  }
}

const Component: React.FC = (): JSX.Element => {
  const { onClick } = useLogic();
  return (
    <p onClick={() => onClick()}>Click here</p>
  );
}
export default Component;

上記のような書き方にすればコンポーネントに書くのはイベント発生の処理のみになるのでComponentのコードの見通しがよくなります。
仕事を通して覚えた方法で、これは頻繁に使っています。というよりかおそらく、これが本来あるべきReact Hooksの形であると思う。
この書き方を覚えて以降、moduleとcomponentの分離というのを意識して書いています。(これはあくまで自分が意識している設計思想です)
コンポーネント内には処理を持たせないように心がけていて、処理はすべてモジュールとして分け、コンポーネントを汚さないようにしています。
モジュールは機能として、コンポーネントはデザインを見せる部分として、それらの役割をきちんと担えるようなコードを書いていくことが重要だと思っています。

簡易Flux

以前書いた記事のReactHooks + Context API によるReduxの実装の派生を少し書いてみる
小技として使える方法でReduxにせずともuseReducerのみを使用してstateを一元管理する方法。

一つのCustomHooksにしてしまって、stateとdispatchのみを使用する。
Reduxにしたい場合はContext APIを活用してProviderで渡してあげるようにすればよいけれど、これはそこまでやらない人むけのやり方。
簡易的にFluxでstateを管理したい場合なんかに有効。

hooks.ts

import React, { useReducer } from "react";

export const useCount = () => {
  type StateType = {
    count: number;
  }

  const initialState: StateType = {
    count: 0
  }

  type ActionType = {
    type : "INCREMENT"|"DECREMENT"|"RESET"
  }

  const reducer = (state: StateType, action: ActionType) => {
    switch (action.type) {
      case "INCREMENT":
        return { ...state, count: state.count + 1 };
      case "DECREMENT":
        return { ...state, count: state.count - 1 };
      case "RESET":
        return initialState;
      default:
        return state;
    }
  }

  const [state, dispatch] = useReducer(reducer, initialState);

  return {
    state,
    dispatch
  }
}

Component.tsx

import React from 'react';
import { useCount } from 'hooks';

const Component: React.FC = (): JSX.Element => {
  const { state, dispatch } = useCount();
  console.log(state);
  return (
    <p onClick={() => dispatch({type: 'INCREMENT'})}>Click here</p>
  );
}
export default Component;

まとめ

正直、React Hooksは掘れば掘るほどいろんなコードの書き方があるので自分に合った思想を探すのがよいです。自分自身はいろんな会社を転々として回って仕事をしているのでいろんな人のデザインパターンを見ることが多いのでその中で自分がよいと思える思想のものを取り入れています。
キャッチアップが大変なJavaScriptだけど、いろんなデザインパターンがあるし飽きない言語だと思います。最近、やっとReactが玩具に思えてきました。

関連記事