Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript & React - one onChange Handler for multiple input fields

Say I have a form with multiple input fields. In normal ES6/React I would create a single method that all input fields would point their onChange handlers to. Something like this:

handleChange(e) {
    e.preventDefault();
    this.setState({[e.target.name]: e.target.value});
}

This helps in the case where you have a lot of form elements and don't have to create a specific method to handle each of them.

Is this possible in TypeScript? Even if it is not type-safe?

like image 875
erik-sn Avatar asked Jan 25 '17 23:01

erik-sn


1 Answers

import React, { useState } from 'react';

const Form = () => {
  const [inputValues, setInputValues] = useState<{ [x: string]: string }>()

  const handleFormSubmit = async (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault()
    const data = {
      name: inputValues?.name,
      email: inputValues?.email,
      phone: inputValues?.phone,
      income: inputValues?.name
    }

    const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    };

    try {
      const response = await fetch('https://xyz/form-submit', requestOptions)
      const res = await response.json()
      console.log(res)
    } catch (error) {
      console.log(error)
    }
  }

  const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
    const { name, value } = e.currentTarget
    setInputValues(prevState => ({ ...prevState, [name]: value }))
  }

  return (
    <div className="Form">
      <div className="form-wrapper">
        <h1>Demo Form for React</h1>
        <form className="form">
          <input 
            className="form-input"
            name="name" 
            value={inputValues?.name || ''} 
            onChange={handleInputChange} 
            placeholder="Your Name"
            type="text"
            data-testid="form-input-name"
          />
          <input 
            className="form-input"
            name="phone" 
            value={inputValues?.phone || ''} 
            onChange={handleInputChange} 
            placeholder="Your Phone" 
            type="tel"
            data-testid="form-input-phone"
          />
          <input 
            className="form-input" 
            name="email"
            value={inputValues?.email || ''} 
            onChange={handleInputChange} 
            placeholder="Your Email" 
            type="email"
            data-testid="form-input-email"
          />
          <input 
            className="form-input"
            name="income"
            value={inputValues?.income || ''} 
            onChange={handleInputChange} 
            placeholder="Your Annual Income" 
            type="number"
            data-testid="form-input-income"
          />
          <button 
            className='form-submit'
            data-testid="form-submit" 
            onClick={handleFormSubmit}
          >
            Submit
          </button>
        </form>
      </div>
    </div>
  );
}

export default Form;

A sample Typescript form. It's features:

  • A single onChange handler
  • One state object into which we can add as many key value pairs without the typescript compiler yelling at us.
  • Uses ES2020 optional chaining.
  • Has data-testid on the DOM elements incase you want to run a few unit test.
  • Should provide autocomplete for the input fields as per their types.
  • A form Submit function sample that uses the fetch api to make a post call to an end point.

Also, with this approach, you don't have to use @ts-ignore, any or make changes to your tsconfig.

Please use it as your hearts desire.

like image 86
Tom Bombadil Avatar answered Sep 21 '22 07:09

Tom Bombadil