skip to Main Content

I am learning typescript + react. I have the following form using reactjs and boostrap:


function ControlGroup(props: ControlGroupProps) {
  const [agentSettings, setAgentSettings] = useState("");
  const [agentQuestion, setAgentQuestion] = useState("");

  return (
    <Form
      onSubmit={(event) =>
        props.onAskAgent(props.agentData, agentSettings, agentQuestion, event)
      }
    >
      <Form.Group className="mb-3" controlId="agentForm.settings">
        <Form.Label>Set your agent settings</Form.Label>
        <Form.Control
          as="textarea"
          rows={3}
          onChange={({ target: { value } }) => setAgentSettings(value)}
          value={agentSettings}
        />
      </Form.Group>
      <Form.Group className="mb-3" controlId="agentForm.question">
        <Form.Label> Ask your question </Form.Label>
        <Form.Control
          as="textarea"
          rows={3}
          onChange={({ target: { value } }) => setAgentQuestion(value)}
          value={agentQuestion}
        />
      </Form.Group>
      <Button className="center" type="submit">
        Ask agent
      </Button>
    </Form>
  );
}

I have two different things I need to capture, agentSettings and agentQuestion. The code below works but I don’t like how I have to type:

{({ target: { value } }) => setAgentSettings(value)}
and then
{({ target: { value } }) => setAgentQuestion(value)}

The code is almost identical other than the last bit. Is there a more elegant way to do this in react?

2

Answers


  1. Valid question. Especially, when there are 10 input fields, you don’t want to write the same code 10 times.

    What’s considered "standard" in this case is to use an object for storing form values and use one event handler for all input field changes, by using name attribute.

    Something like this:

    const [formData, setFormData] = useState({
      inputA: '',
      inputB: '',
    });
    
    ...
    
    const handleChange = (e) => {
      setFormData({
        ...formData,
        [e.target.name]: e.target.value,
      };
    };
    
    
    ...
    <input name="inputA" value={formData.inputA} onChange={handleChangeInput} />
    
    Login or Signup to reply.
  2. I think the best approach when dealing with forms is to use Formik and Yup.
    Formik is the most popular open source form library for React. It saves you lots of time when building forms and offers a declarative, intuitive and adoptable paradigm.
    Yup is an open-source library that integrates perfectly with Formik. It allows you to set all your form validation rules declaratively.
    First install both libraries:

    npm install formik --save
    npm install -S yup
    

    After that:

    import { useFormik } from "formik";
    import * as Yup from 'yup';
    

    The below code would go inside a React.JS component:

    const formik = useFormik({
        initialValues: { firstName: '', email: '', comment: ''},
        onSubmit: (values) => {
        // Handle form submission
        },
        validationSchema: Yup.object({
          firstName: Yup.string().required("Required"),
          email: Yup.string().email("Invalid email address").required("Required"),
          comment: Yup.string().min(25, "Must be 25 characters at minimum").required("Required"),
        }),
      });
    

    The way you would use this function is by spreading the returned object into the input elements:

    <form onSubmit={formik.handleSubmit}>
    <Input
      id="firstName"
      name="firstName"
      {...formik.getFieldProps("firstName")}                 
    />
    <div className="error-message">{formik.errors.firstName}</div>
    
    <Input
      id="email"
      name="email"
      type="email"
      {...formik.getFieldProps("email")}    
    />
    <div className="error-message">{formik.errors.email}</div>
    
    <textarea 
      id="comment" 
      name="comment" 
      rows={4}
      {...formik.getFieldProps("comment")} 
    />
    <div className="error-message">{formik.errors.comment}</div>
    
    <button
    type="submit"
    disabled={!formik.isValid}
    >
      Submit
    </button>
    </form>
    

    Using TypeScript there are a few changes.

    Here you can find Formik TypeScript Support with a very good example for what you’re looking for: https://formik.org/docs/examples/typescript

    Here you can find Yup TypeScript Support: https://github.com/jquense/yup/blob/pre-v1/docs/typescript.md

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search