Skip to main content

React hooks

Reactは簡単にいうと、Webなどの処理と開発を最適化するための新しいエコシステムといえます。 また、hookは関数ベースのみでReactを実装する方法なので、型システムと相性がいいです。

前半では、DOMを直接触らない大体のjsコードをhookで使用する方法をまとめました。 後半では、前半で使ったhookを使って、独自のhookを新たに作る方法をまとめました。

useRefについて

Reactでは、データは一方向(親から子)へ渡されて計算されるが、 親から子のElementのrefに参照することで, 子の要素の値を外から参照したり操作できます。 また、React向けでないライブラリの変数を入れることで、全体が再renderしても初期化されないようにできます。

以下の例で➊では、指定した要素の値を参照するための基本的な使い方です。 ➋では、再renderしたときuseCallbackやChildrenのpropsを変化させず、子要素の再renderを防ぎます ➌では、App全体が再renderしたときに再びインスタンス化されないように値を保持します。

import {useRef, useCallback} from 'react'
const App = ({src="/static/test.png"}) => {
const ref = useRef(null) // ➊:通常のref
const err = useRef(false) // ➋:変化しても再renderしたくない!
const obj = useRef(new Image())// ➌:再renderしても初期化されない!
const onError = useCallback(()=>(err.current=true), [])
const onClick = useCallback(()=> err.current&&window.open(ref.current.src)),[])
return <img {...{src, ref, onClick, onLoad}} />
}

useEffectについて

componentを生成し、Renderしたあとに実行する処理を入れます。 例えば、fetchなど時間がかかる処理を入れることで、ほかの要素のrenderに影響を与えません。

また、第二引数を空の配列にすることで、再renderしたときに再実行したくない重い処理を入れることができます。 React向けではない(DOMを直接触るような)ライブラリは、すべての処理をこの中の入れることで利用できます。

const App = (props) => {
const [data, set] = useState('')
useEffect(()=>{
fetch(props.url).then(res => set(res))
}, [url])
return data && <span>{data}</span>
}

その他

  • useState:値が変化したら、再renderしてほしいような値に使います。(特に表示させる値)
  • useMemo : とりあえずすべての変数をこの中に入れておくと、高速化します。
  • useCallback: とりあえずすべての関数をこの中に入れておくと、高速化します。

自作hookについて

アプリの状態を保存するuseStateでは、前の値を参考に新たな値をsetするときは関数を引数に指定します。 例として、window.location から状態を管理するhookを作成します。 事前に、新しい値に関数を指定できるように、次のような型を定義します。

export type BasicProps<T>  = (()=>T) | T
export type BasicState<T> = ((pre:T)=>T) | T
export type BasicAction<T> = (fn:BasicState<T>) => void

引数の型が関数の場合を最初に除き、useRef内で値を補完することで、 (useStateのset(p=>p)の様な)、過去を参照するhookを作成することができます。 useRefにはデフォルト値と入力値をmergeして入力することで、多くの状態を同時に管理できます。

import {useState, useRef} from 'react'
import {Page, Conf} from '../types'
import {defaultConf, defaultPage} from '../utils'

export const usePage = <T=any>(
props :BasicProps<Partial<Page<T>>>={},
config:BasicProps<Partial<Conf<T>>>={},
) : [
Page<T>,
BasicAction<Partial<Page<T>>>
] => {
if (typeof props === "function")
props = props()
if (typeof config === "function")
config = config()
const pageRef = useRef({...defaultPage, ...props } as Page<T>)
const confRef = useRef({...defaultConf, ...config} as Conf<T>)
const [page, set] = useState(pageRef.current)

const setPage = useCallback(state => {
if (typeof state === "function")
state = state(pageRef.current as Partial<Page<T>>)
pageRef.current = {...pageRef.current, ...state}
set(pageRef.current)
}, [])

return [page, setPage]
}