paint-brush
Resolving React's 'Unable to Type in Input' Issueby@sriram

Resolving React's 'Unable to Type in Input' Issue

by SriramNovember 1st, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Learn to resolve the "Unable to Type in Input" issue in React forms by implementing efficient state management techniques.
featured image - Resolving React's 'Unable to Type in Input' Issue
Sriram HackerNoon profile picture



How to fix the "Unable to type in input" issue in React

How to fix the "Unable to type in input" issue in React

Hello World!


When we work with forms in React, we may encounter an issue where we won't be able to type in the input field/text box if the value property is set. I will be showing what is the actual issue and how I fixed it.

Scenario

Requirement

I have an API that would require three fields, namely Email, Name, and Message, which need to be passed in the body of the API. In the UI, we would be getting those values in an input field or text area and would be using a button to trigger the API call.


The structure expected in the API is something like this:

{
"email": "[email protected]",
"name": "Test",
"message": "Hi, this is a test message."
}

Implementation

In the React app, I've created a component with 2 input fields to get the name and email, a text area for getting the message and a button to trigger the API call and send the 3 values. This is what the component looked like:

import { useState } from "react";
import callApi from "../axios/axios";
import "./contact.scss";
const Contact = () => {
  const payloadInitial = {
    sender: "",
    email: "",
    message: "",
  };
  const initialClass = "form-input";
  const [payload, setPayload] = useState(payloadInitial);
  // Captures Sender name
  const setName = (name) => {
    const temp = payload
    temp.sender = name
    setPayload(temp)
  };
  // Captures Email
  const setEmail = (email) => {
    const temp = payload
    temp.email = email
    setPayload(temp)
  };
  // Captures Message
  const setMessage = (message) => {
    const temp = payload
    temp.message = message
    setPayload(temp)
    checkCanSend();
  };
  // Triggers API call on button click
  const sendMessage = (evt) => {
    evt.preventDefault();
    callApi
      .post("/email", payload)
      .then((res) => {
        console.log("Response: ", res)
      })
      .catch((err) => {
        console.error("Something went wrong: ", err)
      });
  };
  return (
    <div className="form-container">
      <h2 className="form-title">Write to me</h2>
      <div className="form-container">
        <!--Input to get sender name-->
        <label>Your Name</label>
        <input
          type={"text"}
          aria-label="Your Name"
          value={payload.sender}
          onChange={(e) => setName(e.target.value)}
        ></input>
        <!--Input to get sender email-->
        <label>Your Email</label>
        <input
          type={"email"}
          aria-label="Your Email"
          value={payload.email}
          onChange={(e) => setEmail(e.target.value)}
        ></input>
        <!--Textbox to get the message-->
        <label>Type in your message</label>
        <textarea
          rows={8}
          value={payload.message}
          onChange={(e) => setMessage(e.target.value)}
        ></textarea>
        <!--Button to trigger API call-->
        <button onClick={(e) => sendMessage(e)} className="send-button">
          Send Message
        </button>
      </div>
    </div>
  );
};

export default Contact;

In the above component, we are listening to the change of input fields using the onChange event and setting the new values to the payload state.

Issue

The above code was causing an issue where I was not able to type more than one character.

Issue with input field, where it was accepting only one character.

Solution

The solution for the issue is: In the onClick function, instead of storing the state in a temp variable, changing the value in the temp, and setting temp to the state, we should update the state directly.

In our case, let us consider the setName function. The function should be changed like this:

const setName = (name) => {
    setPayload((prev) => ({
      ...prev,
      sender: name,
    }));
    checkCanSend();
  }

Now, the state is updated directly without having any temp variables.


Now, we can type on the input field.

The final code would be:

import { useState } from "react";
import callApi from "../axios/axios";
import "./contact.scss";
const Contact = () => {
  const initialClass = "form-input";
  const [payload, setPayload] = useState(payloadInitial);
  // Captures Sender name
  const setName = (name) => {
    setPayload((prev) => ({
      ...prev,
      sender: name,
    }));
    checkCanSend();
  };
  // Captures Email
  const setEmail = (email) => {
    setPayload((prev) => ({
      ...prev,
      email,
    }));
    checkCanSend();
  };
  // Captures Message
  const setMessage = (message) => {
    setPayload((prev) => ({
      ...prev,
      message,
    }));
    checkCanSend();
  };
  // Triggers API call on button click
  const sendMessage = (evt) => {
    evt.preventDefault();
    callApi
      .post("/email", payload)
      .then((res) => {
        console.log("Response: ", res)
      })
      .catch((err) => {
        console.error("Something went wrong: ", err)
      });
  };
  return (
    <div className="form-container">
      <h2 className="form-title">Write to me</h2>
      <div className="form-container">
        <!--Input to get sender name-->
        <label>Your Name</label>
        <input
          type={"text"}
          aria-label="Your Name"
          value={payload.sender}
          onChange={(e) => setName(e.target.value)}
        ></input>
        <!--Input to get sender email-->
        <label>Your Email</label>
        <input
          type={"email"}
          aria-label="Your Email"
          value={payload.email}
          onChange={(e) => setEmail(e.target.value)}
        ></input>
        <!--Textbox to get the message-->
        <label>Type in your message</label>
        <textarea
          rows={8}
          value={payload.message}
          onChange={(e) => setMessage(e.target.value)}
        ></textarea>
        <!--Button to trigger API call-->
        <button onSubmit={(e) => sendMessage(e)} className="send-button">
          Send Message
        </button>
      </div>
    </div>
  );
};

export default Contact;

Hope this solution is helpful to fix the "Unable to type in the input field" issue. Looking forward to your feedback. Thank you!


Also published here.