Skip to content
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

[React 19] Controlled <select> component is subject to automatic form reset #30580

Open
cjg1122 opened this issue Aug 2, 2024 · 13 comments
Open

Comments

@cjg1122
Copy link

cjg1122 commented Aug 2, 2024

The controlled component also resets the "select" after the action is triggered.
but the "input" component does not.

"use client";
import { useActionState, useState } from "react";
function add() {
  return Date.now();
}
export default function Page() {
  const [state, formAction] = useActionState(add, 0);
  const [name, setName] = useState("");
  const [type, setType] = useState("2");
  return (
    <form action={formAction}>
      <p>{state}</p>
      <p>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </p>
      <p>
        <select
          name="gender"
          value={type}
          onChange={(e) => setType(e.target.value)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
      </p>
      <button>submit</button>
    </form>
  );
}

20240802-193357

Repro: https://codesandbox.io/p/sandbox/stupefied-cohen-n578l6

@ujshaikh
Copy link

ujshaikh commented Aug 2, 2024

@cjg1122
Can you please add actual and expected behavior?

@Deba0099
Copy link

Deba0099 commented Aug 2, 2024

Since useActionState passes the previous state and returns the updated state, we can reset the input and select values accordingly.

Here's an updated version of the code that properly handles resetting both the input and select elements:

"use client";
import { useActionState, useState } from "react";

// This function handles the form submission
function add(previousState, formData) {
  // Process the formData and return the new state
  const newState = Date.now();
  // Reset the input and select values
  formData.reset();
  return newState;
}

export default function Page() {
  // useActionState hook manages the form state and action
  const [state, formAction] = useActionState(add, 0);

  // Local state for input and select values
  const [name, setName] = useState("");
  const [type, setType] = useState("2");

  return (
    <form
      action={(e) => {
        formAction(e);  // Call the form action
        setName("");    // Reset the input value
        setType("2");   // Reset the select value
      }}
    >
      <p>{state}</p>
      <p>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </p>
      <p>
        <select
          name="gender"
          value={type}
          onChange={(e) => setType(e.target.value)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
      </p>
      <button>submit</button>
    </form>
  );
}

Explanation:

  1. add function:

    • Receives previousState and formData as arguments.
    • Processes the form data and returns a new state.
    • Resets the form fields by calling formData.reset(), which is a built-in method to reset form values.
  2. form action handler:

    • Calls formAction to handle the form submission.
    • Resets the local state for the input and select elements to their initial values ("" and "2" respectively).

By incorporating these changes, the form submission will reset both the input and select elements properly.

@ujshaikh
Copy link

ujshaikh commented Aug 2, 2024

I tried with above example and getting this error
image

I think it happed due to mismatching of args for useActionState
const [state, formAction] = useActionState(fn, initialState, permalink?);

src: https://react.dev/reference/react/useActionState

https://codesandbox.io/p/sandbox/react-dev-forked-qvpx9c?file=%2Fsrc%2FPage.js

@cjg1122
Copy link
Author

cjg1122 commented Aug 2, 2024

@ujshaikh The expected behaviour is that the select component should behave the same way as the input component, when I click on the submit button, the action executes and then my 2 components should keep their state values, currently the input component is as expected and the select is reset to its default value instead of the one I chose before I submitted.

I've uploaded a gif of the demo.~

@cjg1122
Copy link
Author

cjg1122 commented Aug 2, 2024

@Deba0099 I tried to setState the local state after the action is submitted, but the select component is still reset to the default value.

@eps1lon eps1lon changed the title [React 19] The controlled component also resets the <select> after the action [React 19] Controlled <select> component is subject to automatic form reset Aug 2, 2024
@eps1lon
Copy link
Collaborator

eps1lon commented Aug 2, 2024

This is indeed a bug, thank you for reporting. Only uncontrolled components should be automatically reset when the action prop is used. The <select> here is controlled though and shouldn't change its value after the form is submitted.

Screen.Recording.2024-08-02.at.13.45.45.mov

-- https://codesandbox.io/p/sandbox/stupefied-cohen-n578l6

@ujshaikh
Copy link

ujshaikh commented Aug 2, 2024

@cjg1122
Tried to reproduce and applied reset
https://codesandbox.io/p/sandbox/react-dev-forked-qvpx9c

@AmanVerma2202
Copy link

AmanVerma2202 commented Aug 4, 2024

@cjg1122
fix: reset both input and select fields to default state after form submission

  • Added useEffect to reset the 'name' input field and 'type' select field to their default states after form action is triggered.
 useEffect(() => {
    setName("");
    setType("2"); 
  }, [state]);
"use client";
import { useActionState, useState, useEffect } from "react";

function add() {
  return Date.now();
}

export default function Page() {
  const [state, formAction] = useActionState(add, 0);
  const [name, setName] = useState("");
  const [type, setType] = useState("2");

  // Reset input and select states when action state changes
  useEffect(() => {
    setName(""); // Reset name input to empty 
    setType("2"); // Reset select to default value 2
  }, [state]);

  return (
    <form action={formAction}>
      <p>{state}</p>
      <p>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </p>
      <p>
        <select
          name="gender"
          value={type}
          onChange={(e) => setType(e.target.value)}
        >
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
      </p>
      <button>submit</button>
    </form>
  );
}
  • fix 👍 : reset both fields to default state after form submission using useRef hook
import { StrictMode, useState, useRef, version } from "react";
import { createRoot } from "react-dom/client";

function App() {
  const [selectValue, setSelectValue] = useState("One");
  const [textValue, setTextValue] = useState("Alpha");
  const formRef = useRef(null);

  return (
    <form
      ref={formRef}
      action={async (formData) => {
        console.log(Object.fromEntries(formData.entries()));
        formRef.current.reset();
        setSelectValue("One");
        setTextValue("Alpha");
      }}
    >
      <label>
        select:
        <select
          name="selectValue"
          onChange={(event) => {
            setSelectValue(event.currentTarget.value);
          }}
          value={selectValue}
        >
          <option>One</option>
          <option>Two</option>
        </select>
        controlled: {selectValue}
      </label>
      <br />
      <label>
        text:
        <input
          name="textValue"
          onChange={(event) => {
            setTextValue(event.currentTarget.value);
          }}
          value={textValue}
        />
        controlled: {textValue}
      </label>
      <br />
      <input type="submit" />
    </form>
  );
}

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <>
    <p>React: {version}</p>
    <App />
  </>
);

check out code 1 here :https://codesandbox.io/p/sandbox/react-dev-forked-vfpgnn?file=%2Fsrc%2FPage.js%3A20%2C31

check out code 2 here : https://codesandbox.io/p/sandbox/react-controlled-select-subject-for-form-reset-forked-ng44jt

@dberardi99
Copy link

Is this bug still present? I could work on it in this case

@AmanVerma2202
Copy link

AmanVerma2202 commented Oct 13, 2024 via email

@cima-alfa
Copy link

This issue is in fact still present. Including 19.0.0-rc.1, 19.0.0-beta-26f2496093-20240514 and 19.0.0-rc-7670501b-20241124

@cima-alfa
Copy link

Now that React 19 is officially out, I can confirm this bug still persists.

@aunruh
Copy link

aunruh commented Dec 10, 2024

yea same here

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

No branches or pull requests

8 participants