thumbnail

【React】custom-hooksのすゝめ

2022/06/01

custom-hooks。 知ってるけど、活用したことが無いという人はReact Hooksをまだ十分に使いこなせていないかもしれません。

custom-hooksとはその名のとおり独自に実装できるReact Hooksです。 既存のReact Hooksを含めた独自の処理を新しいhooksとして定義することが可能です。

このcustom-hooksはかなり有用で、最近のReactプロジェクトではほとんど使われている印象があります。 ぜひ利用をオススメしたいので、今回はcustom-hooksの利点を紹介します。

custom-hooksの利点

custom-hooksの利点は

  • logicviewの分離を簡単にできる
  • logicの再利用が簡単にできる

これに尽きると思います。

custom-hooksの前はlogicviewを分離するために、高階コンポーネントなどを利用している人も多かったと思います。 高階コンポーネントでもよかったのですが、propsの受け渡し部分などを含めて抽象化するのが難しい印象がありました。

custom-hooks自体はただの関数で、コンポーネント内に展開するだけなので、ロジックの分離、共有は高階コンポーネントよりもかなり容易にできると思います。

具体例

custom-hooksを利用することでコンポーネントがどれぐらいスッキリするか具体例を出します。

custom-hooks導入前

例) データ配列をlowpassフィルターにかけてその結果を表示するコンポーネント

Sample.tsx
import React, { useRef, useCallback } from 'react'

import Graph from './Graph'

type Props = {
  freq: number
  q: number
  samplingRate: number
  data: number[]
}

const Sample: React.FC<Props> = ({ freq, q, samplingRate, data }) => {
  const in1 = useRef<number>(0)
  const in2 = useRef<number>(0)
  const out1 = useRef<number>(0)
  const out2 = useRef<number>(0)

  const omega = (2 * Math.PI * freq) / samplingRate
  const alpha = Math.sin(omega) / (2 * q)

  const a0 = 1 + alpha
  const a1 = -2 * Math.cos(omega)
  const a2 = 1 - alpha
  const b0 = (1 - Math.cos(omega)) / 2
  const b1 = 1 - Math.cos(omega)
  const b2 = (1 - Math.cos(omega)) / 2

  const lowpassFilter = useCallback(
    (value: number[]): number[] => {
      let output: number[] = []
      for (let i = 0; i < value.length; i++) {
        const result =
          (b0 / a0) * value[i] +
          (b1 / a0) * in1.current +
          (b2 / a0) * in2.current -
          (a1 / a0) * out1.current -
          (a2 / a0) * out2.current

        in2.current = in1.current
        in1.current = value[i]

        out2.current = out1.current
        out1.current = result

        output.push(result)
      }
      return output
    },
    [freq, q, samplingRate],
  )

  return <Graph data={lowpassFilter(data)} />
}

export default Sample

コンポーネントにロジックがゴリゴリ含まれてて見づらいですね... また、lowpassFilter関数をコンポーネント内に直接書いているので、他コンポーネントでの再利用が難しいことも分かると思います。

custom-hooks導入後

上記のコードに対してcustom-hookを導入したのが以下の例です。

Sample.tsx
import React from 'react'

import Graph from './Graph'
import { useLowpassFilter } from './useLowpassFilter'

type Props = {
  freq: number
  q: number
  samplingRate: number
  data: number[]
}

const Sample: React.FC<Props> = ({ freq, q, samplingRate, data }) => {
  const lowpassFilter = useLowpassFilter(freq, q, samplingRate)

  return <Graph data={lowpassFilter(data)} />
}

export default Sample
useLowpassFilter.ts
import { useRef, useCallback } from 'react'

/**
 * ローパスフィルター関数
 * @param freq - カットオフ周波数
 * @param q - Q値
 * @param samplingRate - サンプリング周波数
 */
export function useLowpassFilter(
  freq: number,
  q: number,
  samplingRate: number,
) {
  const in1 = useRef<number>(0)
  const in2 = useRef<number>(0)
  const out1 = useRef<number>(0)
  const out2 = useRef<number>(0)

  const omega = (2 * Math.PI * freq) / samplingRate
  const alpha = Math.sin(omega) / (2 * q)

  const a0 = 1 + alpha
  const a1 = -2 * Math.cos(omega)
  const a2 = 1 - alpha
  const b0 = (1 - Math.cos(omega)) / 2
  const b1 = 1 - Math.cos(omega)
  const b2 = (1 - Math.cos(omega)) / 2

  const lowpassFilter = useCallback(
    (value: number[]): number[] => {
      let output: number[] = []
      for (let i = 0; i < value.length; i++) {
        const result =
          (b0 / a0) * value[i] +
          (b1 / a0) * in1.current +
          (b2 / a0) * in2.current -
          (a1 / a0) * out1.current -
          (a2 / a0) * out2.current

        in2.current = in1.current
        in1.current = value[i]

        out2.current = out1.current
        out1.current = result

        output.push(result)
      }
      return output
    },
    [freq, q, samplingRate],
  )
  return lowpassFilter
}

LogicとViewを分離できてスッキリしました ✨ 他のコンポーネントでlowpassFilterを使用したい場合は、そのコンポーネント内でuseLowpassFilterを使用するだけで可能です。

例の通り、実装自体はかなり簡単なので是非利用してみることをオススメします 👍

おわり

custom-hooks自体に特別な機能はなく、仕組み自体がかなりシンプルなのも個人的にsuper good 👍

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi