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);
};
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);
};