Skip to content

Instantly share code, notes, and snippets.

@yaakaito
Last active May 1, 2024 06:25
Show Gist options
  • Save yaakaito/8f7419699e44e07852db5c2a2a5ac3e5 to your computer and use it in GitHub Desktop.
Save yaakaito/8f7419699e44e07852db5c2a2a5ac3e5 to your computer and use it in GitHub Desktop.
React 19 Actions

React 19 では、データ更新とそれに伴う一連の流れをうまく扱う方法として Actions が導入されるようです。 例えば、これまでフォームの送信中の状態を次のように扱うことがありましたが、これが React の機能として標準化されます。

const [isPending, setIsPending] = useState(false);
const submit = () => {
  setIsPending(true);
  await foo();
  setIsPending(false);
}
return (
      <form>
        <button click={submit} disable={isPending}>foo</button>
      </form>
    )

新しく導入される Actions の一つである useActionState を利用して書くと次のように書けます。

const [error, submitAction, isPending] = useActionState(
  async () => {
    const error = await foo();
    if (error) { return error; }
    return null;
  }
)
return (
      <form action={submitAction}>
        <button type="submit" disable={isPending}>foo</button>
      </form>
    )

Hook に渡した関数は実質的に submitAction の内容になっていて、isPending はその関数の実行状態に合わせて値が変化します。 これによって最初のコードと同等に isPending を使って UI の状態を変更できる、ということのようです。

useFormStatus

これに組み合わせて使えるものとして useFormStatus という親の form の実行状態を取得することが出来る Hook もあります、これは react-dom から提供されるものです。 例えばボタンコンポーネントを作るときにその状態を外から Props として渡すことがあると思いますが、 Actions を使った form の中ではこれを簡略化することが出来ます。

function FooSubmit() {
  const { pending } = useFormStatus();
  <button type="submit" disable={pending}>foo</button>
}

function FooForm() {
  const [error, submitAction, isPending] = useActionState(
    async () => {
      const error = await foo();
      if (error) { return error; }
      return null;
    }
  )
  return (
        <form action={submitAction}>
          <FooSubmit />
        </form>
      )
}

これが useActionState の例と同じ動作になるようです。

useOptimistic

Actions が失敗したときに巻き戻される useState のようなものです。次にコードで await foo() が失敗すると、 valuecurrentValue に戻ります。

const [value, setValue] = useOptimistic('currentValue')
const [error, submitAction, isPending] = useActionState(
  async () => {
    setValue('newValue')
    const error = await foo();
    if (error) { return error; }
    return null;
  }
)>

ユーザー名を変更したいが、重複チェックに時間がかかるような場合に、結果の予定を先に UI に反映しておくような場合にその実装が標準化できるようです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment