Home > OS >  How to get the value of input tag onSubmit without using onChange in React js/Typescript?
How to get the value of input tag onSubmit without using onChange in React js/Typescript?

Time:03-24

I have no other special events onChange so I need to simplify my code by only handling events onSubmit specifically on the Form tag.

I'm creating a simple Form in Typescript in React and When I click Submit, I want to log the value of the input box in the console. But I get an error when I do this.

import React from "react";

const handleSubmit =(event:React.FormEvent<HTMLFormElement>) =>{
    event.preventDefault();
    console.log(event.currentTarget[0].value);
  };

export const InputForm: React.FC  = () =>{
    return (
        <form className='form-container' onSubmit={handleSubmit}>
            <input className="input" placeholder="Enter value" required />
            <button type="submit">Enter</button>
          </form>  
      )
}

My reasoning for this line => console.log(event.currentTarget[0].value); is that the form tag has 2 elements, input and button, so event.currentTarget[0] alone returns the input tag in the console but it passes an error(Property 'value' does not exist on type 'Element'.ts(2339)) when i try to add .value

CodePudding user response:

The usual thing here is to use a controlled component, but you've said you don't want to do that.

The problem is that event.currentTarget[0] is just of type Element, and Element doesn't have value (which is just on certain elements, like HTMLInputElement, HTMLSelectElement, etc.).

You know it's an Element subtype with value, but TypeScript doesn't, because not all form control elements have value (fieldset and object don't, for a start, but they're counted as form elements).

You can put a utility assertion function in your toolkit:

function assertIsFormFieldElement(element: Element): asserts element is HTMLInputElement | HTMLSelectElement | HTMLButtonElement {
// Customize this list as necessary −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    if (!("value" in element)) {
        throw new Error(`Element is not a form field element`);
    }
}

...then use it:

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) =>{
    event.preventDefault();
    const firstField = event.currentTarget[0];
    assertIsFormFieldElement(firstField);
    console.log(firstField.value);
};

Playground link


Side note: If you're going to do this, you might consider putting a name on the input and using that when looking up the element, rather than assuming the input will be the first element:

<input name="the-name" className="input" placeholder="Enter value" required />

Then you can use a utility function to get the named item from the form:

function getFormControl(form: HTMLFormElement, name: string): HTMLInputElement | HTMLSelectElement | HTMLButtonElement /* | ...*/ {
    const control = form.elements.namedItem(name);
    if (!control || control instanceof RadioNodeList || !("value" in control)) {
        throw new Error(`Form control "${name}" not found or was a RadioNodeList`);
    }
    return control;
} 

// ...


const handleSubmit = (event: React.FormEvent<HTMLFormElement>) =>{
    event.preventDefault();
    const inputField = getFormControl(event.currentTarget, "the-name");
    console.log(inputField.value);
};

Playground link

  • Related