【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の利点は
- logicとviewの分離を簡単にできる
- logicの再利用が簡単にできる
これに尽きると思います。
custom-hooksの前はlogicとviewを分離するために、高階コンポーネントなどを利用している人も多かったと思います。 高階コンポーネントでもよかったのですが、propsの受け渡し部分などを含めて抽象化するのが難しい印象がありました。
custom-hooks自体はただの関数で、コンポーネント内に展開するだけなので、ロジックの分離、共有は高階コンポーネントよりもかなり容易にできると思います。
具体例
custom-hooksを利用することでコンポーネントがどれぐらいスッキリするか具体例を出します。
custom-hooks導入前
例) データ配列をlowpassフィルターにかけてその結果を表示するコンポーネント
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を導入したのが以下の例です。
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
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 👍