※この記事は React でも Vue っぽい実装したい!という思いを元に作っています。著者は React も Vue も好きです。
普段 Vue を使っている著者は、ReactでのStateの変更をやや煩雑に感じてしまいます。
この記事では、ReactでもVueのようなリアクティブを提供するHooksの実装を紹介します。
小規模で、フォームフィールドが多いようなアプリケーションではコードの見通しをよくできるのではないかなと思います。
ReactでStateを<input />
で変更できるようにしようとすると、次のような実装になります。
JavaScriptconst App = () => {
const [value, setValue] = useState('');
return (
<input
defaultValue={value}
onInput={(e) => setValue(e.target.value)}
/>
);
};
defaultValue
で初期値を指定して、onInput
で入力値を同期するという実装が必要です。
これを Vue で書くと次のようになります。
Plain Text<template>
<input v-model="value" />
</template>
<script>
export default {
data: {
value: '',
}
}
</script>
周辺がごちゃごちゃしますが、本質的にはv-model="value"
のみです。
このリアクティブが個人的に思う Vue の強みです。
Vue っぽくするためには、<input />
に Props を指定したら、 <input />
側で State を変更できるようにしなければなりません。
ここでは、useReactiveState
という値と更新関数を一つの State に持たせる Hooks を作成します。
/hooks/index.tsimport { useState } from 'react';
export type ReactiveState<T> = {
value: T;
update: (value: T) => void;
};
export const useReactiveState = <T>(initialValue: T): ReactiveState<T> => {
const [value, setValue] = useState<T>(initialValue);
return {
value,
update: setValue,
};
};
この Hooks は state.value
で State の値にアクセスでき、state.update(newValue)
で State を更新できます。
ReactiveState
を Props に取り、コンポーネント内で状態の更新まで行うInputライクなコンポーネント、AppInput
を作成します。
/components/AppInput.tsximport React, { FC } from 'react';
import { ReactiveState} from '../hooks';
type Props = {
value: ReactiveState;
};
export const AppInput: FC<Props> = ({ value }) => {
const onInput = (e: FormEvent<HTMLInputElement>) => {
if (e.target instanceof HTMLInputElement) {
value.update(e.target.value);
}
};
return <input
defaultValue={value.value}
onInput={onInput}
/>;
};
このコンポーネントでは<input />
のonInput
のハンドラーをコンポーネント内に閉じ込め、コンポーネントから直接 Props を更新しています。
作成したuseReactiveState
とAppInput
を使ってみましょう。
/containers/App.tsximport { useReactiveState } from '../hooks';
import { AppInput } from '../components/AppInput';
export const App = () => {
const name = useReactiveState('');
return (
<div>
<AppInput value={name} />
<p>{name.value}</p>
</div>
);
};
useReactiveState
を使用して生成したname
をAppInput
のvalue
として指定しています。
これだけでname
がAppInput
の入力状態と同期できます。
パスポートのMRZをシミュレートするツールで、Inputとのデータのやり取りでuseReactiveState
を使用しました。
使用箇所が少ないとあまり旨味がありませんが、フォームがたくさんあるようなアプリケーションでは Input ひとつひとつの記述がシンプルになります。
ただ、値の表示がstate.value
とプロパティを指定しなければならないのが直感的ではないので、もっと良い方法がないか模索中です。