From d63fe7081aa96069b14e639799c1e5c0528725d5 Mon Sep 17 00:00:00 2001 From: Tim Whitney Date: Thu, 22 Aug 2024 09:24:35 -0600 Subject: [PATCH 1/2] fix: Update useActionState to add isPending return --- src/content/reference/react/useActionState.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/content/reference/react/useActionState.md b/src/content/reference/react/useActionState.md index 2d4c7958761..ac63cead184 100644 --- a/src/content/reference/react/useActionState.md +++ b/src/content/reference/react/useActionState.md @@ -7,7 +7,7 @@ title: useActionState `useActionState` is a Hook that allows you to update state based on the result of a form action. ```js -const [state, formAction] = useActionState(fn, initialState, permalink?); +const [state, formAction, isPending] = useActionState(fn, initialState, permalink?); ``` @@ -39,7 +39,7 @@ async function increment(previousState, formData) { } function StatefulForm({}) { - const [state, formAction] = useActionState(increment, 0); + const [state, formAction, isPending] = useActionState(increment, 0); return (
{state} @@ -98,10 +98,11 @@ function MyComponent() { } ``` -`useActionState` returns an array with exactly two items: +`useActionState` returns an array with exactly three items: 1. The current state of the form, which is initially set to the initial state you provided, and after the form is submitted is set to the return value of the action you provided. 2. A new action that you pass to `` as its `action` prop. +3. A boolean isPending. Will be false when action is complete or not taken; it will be true when pending. When the form is submitted, the action function that you provided will be called. Its return value will become the new current state of the form. From 4ac144537d6fc6d7cb6d45c50a1ba3a3e3db87f8 Mon Sep 17 00:00:00 2001 From: Tim Whitney Date: Thu, 22 Aug 2024 09:53:26 -0600 Subject: [PATCH 2/2] fix: Add more documentation for the handling of isPending and the use on non-form items. --- src/content/reference/react/useActionState.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/content/reference/react/useActionState.md b/src/content/reference/react/useActionState.md index ac63cead184..d14a3834757 100644 --- a/src/content/reference/react/useActionState.md +++ b/src/content/reference/react/useActionState.md @@ -115,6 +115,33 @@ function action(currentState, formData) { } ``` +`useActionState` can also be used outside of the form element + +```js +import { useActionState, useRef } from "react"; + +function Form({ someAction }) { + const ref = useRef(null); + const [state, action, isPending] = useActionState(someAction); + + async function handleSubmit() { + // See caveats below + await action({ email: ref.current.value }); + } + + return ( +
+ + + {state.errorMessage &&

{state.errorMessage}

} +
+ ); +} +``` + + #### Display form errors {/*display-form-errors*/} @@ -289,3 +316,70 @@ function action(currentState, formData) { // ... } ``` + +### My handler didn't trigger isPending until my action was called +The pending state begins when the return `action` is dispatched and will revert back after all actions and transitions have settled. The mechanism for this under the hook is the same as `useOptimisitic`. + +Concretely, what this means is that the pending state of `useActionState` will not represent any actions or sync work performed before dispatching the action returned by `useActionState`. + +To Solve place all function calls inside the action: +```js +import { useActionState, useRef } from "react"; + +function Form({ someAction, someOtherAction }) { + const ref = useRef(null); + const [state, action, isPending] = useActionState(async (data) => { + // Pending state is true already. + await someOtherAction(); + return someAction(data); + }); + + async function handleSubmit() { + // The pending state starts at this call. + await action({ email: ref.current.value }); + } + + return ( +
+ + + {state.errorMessage &&

{state.errorMessage}

} +
+ ); +} +``` + +Or for greater control, you can also wrap both in a transition and use the isPending state of the transition: + +```js +import { useActionState, useTransition, useRef } from "react"; + +function Form({ someAction, someOtherAction }) { + const ref = useRef(null); + + // isPending is used from the transition wrapping both action calls. + const [isPending, startTransition] = useTransition(); + + // isPending not used from the individual action. + const [state, action] = useActionState(someAction); + + async function handleSubmit() { + startTransition(async () => { + // The transition pending state has begun. + await someOtherAction(); + await action({ email: ref.current.value }); + }); + } + + return ( +
+ + + {state.errorMessage &&

{state.errorMessage}

} +
+ ); +}``` \ No newline at end of file