Material UIを使ってフォームを作成するとき、テキストフィールドなどはコンポーネント化しちゃいますよね。
親コンポーネント(入力フォームのページ)ではuseStateで入力値を管理する。
その時に、子コンポーネント(テキストフィールド)にonChange用の関数を渡すので、useCallbackでsetState関数をメモ化して上げる必要があります。
import React from 'react'
import TextField from '@material-ui/core/TextField'
interface TextInputProps {
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
value: string
}
const TextInput = (props: TextInputProps) => {
return (
<TextField
value={props.value}
type={"text"}
onChange={props.onChange}
/>
)
}
export default TextInput
import React, {FC, useEffect, useState} from 'react';
import TextInput from 'src/components/TextInput'
const Hoge: FC () => {
const [name, setName] = useState<string>('');
const inputName = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value)
},[setName])
return (
<form>
<TextInput onChange={inputName} value={name} />
</form>
)
}
このようなテキストフィールドが1つならいいけど、複数あると、毎回useCallbackを作るのがバリ面倒です。
なので、useCallbackでメモ化された関数を作るカスタムフックを作成しました。
import React, {useCallback, SetStateAction, Dispatch} from 'react'
export const useStringChangeEventCallback = (update: Dispatch<SetStateAction<string>>) => {
return useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
update(event.target.value)
},[update])
}
こうして使います。
import React, {FC, useEffect, useState} from 'react';
import TextInput from 'src/components/TextInput'
import {createStringChangeEventCallback} from 'src/lib/customHooks'
const Hoge: FC () => {
const [name, setName] = useState<string>(''),
[address, setAddress] = useState<string>(''),
[companyName, setCompanyName] = useState<string>('');
return (
<form>
<TextInput
onChange={useStringChangeEventCallback(setName)}
value={name}
/>
<TextInput
onChange={useStringChangeEventCallback(setAddress)}
value={address}
/>
<TextInput
onChange={useStringChangeEventCallback(setCompanyName)}
value={companyName}
/>
</form>
)
}
これで入力フィールドがたくさん必要なフォームだとしても、スッキリ書けますね!!!
数値型の入力フィールドならこんな関数を用意しておくのもアリ。
export const useNumberChangeEventCallback = (update: Dispatch<SetStateAction<number>>) => {
return useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
if (/^[0-9]+$/.test(event.target.value)) {
// event.target.valueは文字列型なので、数値型に変換する
update(Number(event.target.value))
}
}, [update])
}
2020/09/19追記
Reactコンポーネント以外で useCallback
などのHooksを使う場合、eslintに react-hooks/rules-of-hooks
のルールを入れているとエラーになります。
縛りを緩めることになりますが、この方法を採用するなら、lintのルールから抜いてください。
2020/10/20追記
関数名を修正。
そもそもカスタムフックを作るときは use~~~ という関数名で作れば、上記lintで怒られることもありませんでした。
エラー文とドキュメントはよく読もう。(自戒)