skip to Main Content

Consider this example (CodeSandbox):

import { useState } from "react";

export default () => {
  const [email, setEmail] = useState("");

  return (
    <form method="post" action="/example">
      <input type="text" name="first-name" />
      <input type="text" name="last-name" />
      <input
        type="email"
        value={email}
        onChange={(e) => {
          setEmail(e.target.value);
        }}
      />
      <button type="submit">submit</button>
    </form>
  );
};

If you type in "foo" for the first name, "bar" for the last name, and "[email protected]" for the email, the payload that is submitted to the server is first-name=foo&last-name=bar, not first-name=foo&last-name=bar&[email protected]. This is because email is a controlled component whereas the other two are uncontrolled.

The most obvious thing to do would be to refactor the form to use all controlled components, but I’m looking for a way that would be easier and less time consuming.

2

Answers


  1. Chosen as BEST ANSWER

    One option would be to hijack the onSubmit event like this (CodeSandbox):

    import { useState } from "react";
    
    export default () => {
      const [email, setEmail] = useState("");
      const handleSubmit = async (e) => {
        e.preventDefault();
    
        const firstName = e.target.querySelector("[name=first-name]").value;
        const lastName = e.target.querySelector("[name=last-name]").value;
        const formData = new FormData();
    
        formData.append("firstName", firstName);
        formData.append("lastName", lastName);
        formData.append("email", email);
    
        console.log(formData);
        // FormData(3) { firstName → "foo", lastName → "bar", email → "[email protected]" }
    
        const response = await fetch('/example', {
          method: 'post',
          body: formData,
        });
        // handle response
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" name="first-name" />
          <input type="text" name="last-name" />
          <input
            type="email"
            value={email}
            onChange={(e) => {
              setEmail(e.target.value);
            }}
          />
          <button type="submit">submit</button>
        </form>
      );
    };
    

    At a high level, the idea is to:

    1. Construct the FormData that otherwise would have been sent to the server.
    2. Add the data from the controlled component to the FormData.
    3. Submit to the server.

    It's not quite plug-n-play, but at least you don't have to spend the time making the existing uncontrolled components controlled. (1) is probably the most cumbersome, but it doesn't seem too bad.


  2. Looks like I just forgot the name attribute!

    <input
      type="email"
      name="email"
      value={email}
      onChange={(e) => {
        setEmail(e.target.value);
      }}
    />
    

    With the above, the payload that is submitted to the server is like so, as expected:

    first-name=foo&last-name=bar&email=baz%40example.com
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search