Laravel + Inertia で開発する場合、フォームの送信はInertiaのuseFormヘルパーを使うことが多いかと思います。
GitHub
Reactで複数のsetDataを連続して使うときにうまく動作しない現象がありました。
例えば、ボタンを押すと複数のinputの数字がカウントアップするという画面をイメージしてください。マニュアル通りに記述すると下記のようになります。
import { useForm } from '@inertiajs/react';
export default function Page() {
const { data, setData } = useForm({
a: 1,
b: 1,
c: 1,
});
const countUp = () => {
setData('a', data.a + 1);
setData('b', data.b + 1);
setData('c', data.c + 1);
}
return (
<>
<button onClick={countUp}>
Count up
</button>
<input value={data.a} />
<input value={data.b} />
<input value={data.c} />
</>
);
}
しかし、この書き方だと最後のcだけカウントアップされて、aとbは変化しません。
このように、複数のsetDataを連続して使うと、最後のsetDataしか動作しません。
解決策
解決策は、setDataをこのように記述します。
const countUp = () => {
setData(data => ({...data, a: data.a + 1}));
setData(data => ({...data, b: data.b + 1}));
setData(data => ({...data, c: data.c + 1}));
}
これで、すべてのsetDataが正しく動作し、a、b、c全てがきちんとカウントアップされます。どうやらこれはバグではなく、Reactのstateの挙動によるものらしいです。
prevStateを使わない場合は、新しい値をオブジェクトにして渡すこともできます。ただし、この書き方ではdataオブジェクトがそのまま上書きされるのでご注意ください。setDataに渡されない値(下記の場合はc)はdataオブジェクトから削除されます。
const countUp = () => {
setData({
a: 2,
b: 2,
});
}
Laravel Precognitionでの記述方法
なお、Laravel PrecognitionのuseFormヘルパーでも同じ現象が起きます。Precognitionの場合は下記のように記述します。
import { useForm } from 'laravel-precognition-react-inertia';
export default function Page() {
const form = useForm('post', route('posts.store'), {
a: 1,
b: 1,
c: 1,
});
type DataType = typeof form.data;
const countUp = () => {
form.setData((data: DataType) => ({...data, a: data.a + 1}));
form.setData((data: DataType) => ({...data, b: data.b + 1}));
form.setData((data: DataType) => ({...data, c: data.c + 1}));
}
...
TypeScriptの場合、タイプエラーが表示されるので型を定義してあげます。
コンソールエラーなども出してくれないので、原因の追求にめちゃめちゃ苦労しました。
以上です。
コメント