8000 Bug: useFormStatus pending state is reset when component state is updated · Issue #30368 · facebook/react · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Bug: useFormStatus pending state is reset when component state is updated #30368

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jatwood opened this issue Jul 18, 2024 · 10 comments
Open

Comments

@jatwood
Copy link
jatwood commented Jul 18, 2024

If you have a component that relies on the pending return value of useFormStatus, the pending state will incorrectly reset to false if the component is updated due to a useState update. This does not happen if the useState hook is placed in a child component.

React version: 19.0.0-rc-512b09b2-20240718

Steps To Reproduce

codesandbox.io/p/sandbox/react-useformstatus-pending-reset-on-unrelated-state-update-m59zw8

  1. Create a react app that uses form actions (this uses NextJS starter code)
  2. Have the form action delay for a set period of time before resolving
  3. Create a child component that uses useFormStatus().pending. Have the component also use a useState hook that updates on an interval using useEffect.
  4. Place this child component as a child of the <form /> element
  5. Verify that useFormStatus().pending is reset to false as soon as a call to setState happns

Example component


export default function SubmissionState() {
  const formStatus = useFormStatus();
  const [counter, setCounter] = useState(0);
  useEffect(() => {
    const id = setTimeout(() => {
      if (!formStatus.pending) {
        setCounter(0);
        return;
      }
      setCounter(counter + 1);
    }, 1000);
    return () => clearTimeout(id);
  }, [counter, formStatus.pending]);
  return (
    <div>
      {formStatus.pending ? `Pending for ${counter} seconds` : "Not pending"}
    </div>
  );
}

this example works as expected, useFormStatus().pending state matches the server actions state

function SubmissionStateBody({ pending }: { pending: boolean }) {
  const [counter, setCounter] = useState(0);
  useEffect(() => {
    if (!pending) {
      setCounter(0);
      return;
    }
    const id = setTimeout(() => {
      setCounter(counter + 1);
    }, 1000);
    return () => clearTimeout(id);
  }, [counter, pending]);
  return (
    <div>{pending ? `Pending for ${counter} seconds` : "Not pending"}</div>
  );
}

export default function SubmissionStateCorrect() {
  const formStatus = useFormStatus();
  return <SubmissionStateBody pending={formStatus.pending} />;
}

Link to code example:

Github repro using nextjs starter template

The current behavior

The UI shows the submission as pending for the length of the server action.

The expected behavior

The UI shows the submission as not-pending as soon as the counter is updated.

@jatwood jatwood added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jul 18, 2024
@jatwood jatwood changed the title Bug: useFormStatus pending state reset component is when component state is updated Bug: useFormStatus pending state is reset when component state is updated Jul 18, 2024
@Aman37773
Copy link

Basically, if you want it to show status as not-pending once counter is updated then create another state variable(lets say pendinggg) whose initial value is same as that of pending and you immediately have to set pendinggg as false once counter is updated and inside useeffect have a condition that once pending is false then not to re-run the useeffect.

import React, { useState, useEffect } from 'react';
const useFormStatus = () => {
return { pending: true }; // Example status, replace with actual implementation
};
function SubmissionStateBody({pending}) {
const [counter, setCounter] = useState(0);
const [localPending, setLocalPending] = useState(pending);
useEffect(() => {
let id;
if (localPending) {
id = setTimeout(() => {
setCounter((prevCounter) => prevCounter + 1);
setLocalPending(false); // Stop showing pending as soon as the counter updates
}, 1000);
} else {
setCounter(0);
}

return () => clearTimeout(id);

}, [localPending, counter]);

return

{localPending ? Pending for ${counter} seconds : 'Not pending'}
;
}

export default function SubmissionStateCorrect() {
const formStatus = useFormStatus();
return ;
}
thats the code
here once counter updates, status becomes not pending..

@jatwood
Copy link
Author
jatwood commented Jul 19, 2024

@Aman37773 This issue is about a bug< 8000 code class="notranslate">useFormStatus which is a hook in react-dom.

@eps1lon
Copy link
Collaborator
eps1lon commented Jul 22, 2024

Can be reproduced with latest RC and just Client Actions: https://codesandbox.io/p/sandbox/react-useformstatus-pending-reset-on-unrelated-state-update-m59zw8

@eps1lon eps1lon added Type: Bug React 19 and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels Jul 22, 2024
@ujshaikh
Copy link
ujshaikh commented Aug 1, 2024

@eps1lon
I would like to pick this bug, can you please assign it to me?

@eps1lon
Copy link
Collaborator
eps1lon commented Aug 1, 2024

@ujshaikh We don't assign people to issues since they might stop working on the issue and then it looks like the issue is still being worked on. Assigning issues hasn't helped us manage issues. We just recommend to people start working on a bug immediately.

@lopesmartinz
Copy link
lopesmartinz commented Oct 28, 2024

Any news on this topic? I recently started using React 19 RC as part of the upgrade of Next.js to v15 and I'm facing this issue in most places where I'm using useFormStatus. If we don't get a fix for this issue I'll have to think of getting rid of useFormStatus and replace it with a custom solution.

@yudistiraashadi
Copy link

Hi. I still have this bug on the stable React 19.0.1

@Kreynux
Copy link
Kreynux commented Feb 5, 2025

I have a similar issue.
"pending" does not work if a child-component updates its own state (useState), and the form is submitted without that child-component being rerendering at least once before submitting (you initiate the form and submit right away). I printed the value of pending to the console log and It looks like pending becomes true but changes back to false immediately (before submit action is finished).

This code (from a child component in a form) breaks pending from useFormStatus:

const [error, setError] = useState("")

  useEffect(() => {
    validate(inputRef.current)
  }, [])

  const validate = (target?: HTMLInputElementRef | null) => {
    if (target?.validity.valueMissing) {
      setError(emptyTextReferenceError)
    } else if (target?.validity.patternMismatch) {
      setError(shortTextReferenceError)
    } else {
      setError("")
    }
  }

It works if if comment out setError or if I change from useEffect to useLayoutEffect.
It also work if the input gets rerender at least once before submitting.

@redbmk
Copy link
redbmk commented Mar 20, 2025

@eps1lon Any chance you'd be able to take a look at @vordgi's PR, #30718? This bug still exists in the latest canary build (updated fork of your codesandbox) and is preventing me from being able to update to NextJS 15 and React 19. It looks like a pretty simple fix and there's a test included, but the PR went stale.

I'm not able to get the workaround @jatwood mentioned to work either. This also likely is the root cause of #31363 considering any non-native select component is likely to make state changes.

@rwieruch
Copy link

I can confirm that this bug exists. My workaround at the moment is using useActionState's pending state instead. But it would be great to get useFormStatus stay stable.

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

Successfully merging a pull request may close this issue.

9 participants
0