React JS - Working With Forms

Vitthal KorvanVitthal Korvan
10 min read

Form Elements

1. Working with input field in Forms

import { useState } from 'react'
import './App.css'

function App() {
  const [username, setUsername]=useState("")
//default value is empty string

  function handleUsername(e){
    console.log(e.target.value);
    setUsername(e.target.value)
  }
  return (
    <>
      <h2>Form Basics</h2>
      <label htmlFor="username">User Name </label>
      {/* we use htmlFor for label we can't use for 
        because it is reserved keyword in JS */}
      <input type="text" id='username' value={username} 
        onChange={handleUsername}/>
      <p>UserName: {username}</p>
    </>
  )
}

export default App
import { useState } from 'react'
import './App.css'

function App() {
  const [username, setUsername]=useState("")
  return (
    <>
      <h2>Form Basics</h2>
      <label htmlFor="username">User Name </label>
      <input type="text" id='username' value={username} 
        onChange={(e)=>setUsername(e.target.value)} />
      <p>UserName: {username}</p>
    </>
  )
}

export default App

How It Works

  1. useState hook:

     const [username, setUsername] = useState("");
    
    • useState creates a state variable called username and sets its initial value to an empty string ("").

    • setUsername is a function that allows you to update the value of username.

    • This is how React knows what the current value of username is, and it will automatically re-render the component when username changes.

  2. handleUsername function:

     function handleUsername(e) {
       console.log(e.target.value);
       setUsername(e.target.value);
     }
    
    • e represents the event object, and e.target refers to the input field that triggered the event.

    • e.target.value gets the current value from the input field (whatever the user is typing).

    • setUsername(e.target.value) updates the username state with the new input value.

    • Every time the input value changes, the username state gets updated, and the component re-renders to display the updated value.

  3. JSX (Rendering the UI):

     <input type="text" id='username' value={username} 
     onChange={handleUsername}/>
     <p>UserName: {username}</p>
    
    • The input field has its value tied to the username state. This means whatever is in username will be displayed in the input field.

    • The onChange={handleUsername} triggers the handleUsername function whenever the user types something.

    • The <p> tag displays the current username value as text right below the input field.

Flow:

  • Initially, username is an empty string ("").

  • When the user types in the input box, the handleUsername function is triggered, updating username with the new input value.

  • React re-renders the component to show the updated username both in the input field and in the <p> element.

// Multiple Input Field Using a Button
import { useState } from "react";
import "./App.css";

function App() {
  const [username, setUsername] = useState(""); 
  const [password, setPassword] = useState("")

  function handleSubmit(e) {
    e.preventDefault();
    console.log('Hey! ' ,username);
  }
  return (
    <>
      <h2>Form Basics</h2>
      <form onSubmit={handleSubmit}>
        <label htmlFor="username">User Name </label>
        <input
          type="text"
          id="username"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        <input
          type="password"
          id="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <button type="submit">Submit</button>
      </form>
      <p>UserName: {username}</p>
    </>
  );
}
export default App;

2. For Radio Buttons:

// Radio Button
import { useState } from 'react'

function App() {
  const [gender, setGender] = useState('')
  return (
    <>
      <input
        type="radio"
        checked={gender === "male"}
        onChange={() => {
          setGender("male");
        }}
      />
      <label htmlFor="male">Male</label>

      <input
        type="radio"
        checked={gender === "female"}
        onChange={() => {
          setGender("female");
        }}
      />
      <label htmlFor="female">Female</label>

      <input
        type="radio"
        checked={gender === "others"}
        onChange={() => {
          setGender("others");
        }}
      />
      <label htmlFor="others">Others</label>

    </>
  );
}

export default App

How it works

  1. useState hook:

     const [gender, setGender] = useState('');
    
    • This initializes the gender state as an empty string ('').

    • setGender is a function used to update the gender state whenever the user selects a radio button.

  2. Radio buttons: Each radio button's checked attribute is linked to the current value of the gender state, and when clicked, the state is updated with the selected gender:

     <input
       type="radio"
       checked={gender === "male"}
       onChange={() => { setGender("male"); }}
     />
     <label htmlFor="male">Male</label>
    
    • The checked={gender === "male"} ensures that the radio button for "Male" is selected only when the gender state is "male".

    • onChange={() => { setGender("male"); }} updates the gender state to "male" when the user selects this radio button.

Similarly, for "Female" and "Others":

    <input
      type="radio"
      checked={gender === "female"}
      onChange={() => { setGender("female"); }}
    />
    <label htmlFor="female">Female</label>

    <input
      type="radio"
      checked={gender === "others"}
      onChange={() => { setGender("others"); }}
    />
    <label htmlFor="others">Others</label>
  1. How it behaves:

    • Only one radio button can be selected at a time. When a user clicks on a radio button, the gender state is updated (e.g., "male", "female", or "others"), and the corresponding radio button becomes checked.

    • The checked attribute for each radio button ensures that it visually reflects the current state.

3. Handling Multiple Inputs:

import { useState } from "react";

function HandleMultipleInputs() {
  const [formData, setFormData] = useState({
    firstName: "",
    lastName: "",
    email: "",
    userName: "",
    password: "",
    confirmPassword: "",
    phone: "",
    address: "",
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(formData);
  };

  const handleChange = (e) => {
    setFormData((prevState) => {
      return { ...prevState, [e.target.id]: e.target.value };
    });
  };

  const {
    firstName,
    lastName,
    email,
    userName,
    password,
    confirmPassword,
    phone,
    address,
  } = formData;

  return (
    <form onSubmit={handleSubmit} style={{ padding: "1rem" }}>
      <div className="formGroup">
        <label htmlFor="firstName">firstName</label>
        <br />
        <input
          type="text"
          name="firstName"
          id="firstName"
          value={firstName}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="lastName">lastName</label>
        <br />
        <input
          type="text"
          name="lastName"
          id="lastName"
          value={lastName}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="userName">userName</label>
        <br />
        <input
          type="text"
          name="userName"
          id="userName"
          value={userName}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="email">email</label>
        <br />
        <input
          type="email"
          name="email"
          id="email"
          onChange={handleChange}
          value={email}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="password">password</label>
        <br />
        <input
          type="password"
          name="password"
          id="password"
          value={password}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="confirmPassword">confirm Password</label>
        <br />
        <input
          type="password"
          name="confirmPassword"
          id="confirmPassword"
          value={confirmPassword}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="phone">phone</label>
        <br />
        <input
          type="number"
          name="phone"
          id="phone"
          value={phone}
          onChange={handleChange}
        />
      </div>
      <div className="formGroup">
        <label htmlFor="address">address</label>
        <br />
        <input
          type="text"
          name="address"
          id="address"
          value={address}
          onChange={handleChange}
        />
      </div>
      <input type="submit" value="sign up" style={{ marginTop: "1rem" }} />
    </form>
  );
}
export default HandleMultipleInputs;

How it works

  1. State Initialization:

     const [formData, setFormData] = useState({
       firstName: "",
       lastName: "",
       email: "",
       userName: "",
       password: "",
       confirmPassword: "",
       phone: "",
       address: "",
     });
    
    • The form data is stored in an object formData with keys for each form field (e.g., firstName, lastName, email).

    • setFormData is used to update this object when any of the form fields change.

  2. handleChange function:

     const handleChange = (e) => {
       setFormData((prevState) => {
         return { ...prevState, [e.target.id]: e.target.value };
       });
     };
    
    • This function is called whenever a user types in any of the form fields.

    • e.target.id refers to the id of the input field that triggered the event, and e.target.value is the new value entered.

    • setFormData updates the corresponding field in the formData object while keeping the other fields unchanged (...prevState spreads the previous state to avoid overwriting the entire object).

  3. handleSubmit function:

     const handleSubmit = (e) => {
       e.preventDefault();
       console.log(formData);
     };
    
    • This function prevents the default form submission behavior (which would refresh the page) and logs the formData to the console when the form is submitted.

    • In a real application, this is where you would handle the submission, like sending the data to a backend.

  4. JSX (Rendering the form): Each input field corresponds to a key in the formData object, and its value is controlled by React:

     <input
       type="text"
       name="firstName"
       id="firstName"
       value={firstName}
       onChange={handleChange}
     />
    
    • The id of each input matches the key in the formData object (e.g., id="firstName").

    • value={firstName} ensures that the input field reflects the current state.

    • onChange={handleChange} updates the formData when the user types something.

The same pattern applies to all the other form fields (last name, email, phone, etc.).

  1. Submit Button:

     <input type="submit" value="sign up" style={{ marginTop: "1rem" }} />
    
    • A submit button is provided at the bottom of the form. When clicked, it triggers the handleSubmit function.

Key Concepts:

  • Single State Object: All form fields are managed using one state object (formData), making the code more concise and scalable.

  • Dynamic Updates: The handleChange function dynamically updates the form fields based on the id of the input element, making it reusable for all fields.

  • Controlled Components: The form inputs are controlled components because their values are managed by React's state.

useRef Hook

useRef always returns single object and it only has current value of the object. In useRef the component value doesn't re-render. Change of ref does not trigger re-render of the component.

Form Input Using useRef

import React, { useRef } from "react";

function ExamplUseRef() {
  console.log("component Rendered");
  const username = useRef("Vitthal");
  const handleName = () => {
    username.current = "Korvan";
    console.log(username);
  };
  return (
    <>
      <h1>Hello, {username.current}</h1>
      <button onClick={handleName}>Change UserName</button>
    </>
  );
}

export default ExamplUseRef;
import React, { useRef } from "react";

function DOMUseRef() {
  const h1ref = useRef();
  const handleClick = () => {
    const h1Element = h1ref.current;
    console.log(h1Element);
    h1Element.textContent = "Hey! Vitthal";
    h1Element.style.backgroundColor = "blue";
    h1Element.style.color = "white";
  };
  return (
    <>
      <h1 ref={h1ref}>Hello! World</h1>
      <button onClick={handleClick}> Change Content</button>
    </>
  );
}
export default DOMUseRef;

How it works

  1. useRef Hook:

     const username = useRef("Vitthal");
    
    • useRef is initialized with "Vitthal", meaning the username.current is set to "Vitthal".

    • Unlike useState, changing the value of a useRef variable does not trigger a re-render. Instead, the value is simply stored and persists across renders.

    • username.current holds the current value ("Vitthal" at the start).

  2. handleName function:

     const handleName = () => {
       username.current = "Korvan";
       console.log(username);
     };
    
    • This function changes the value of username.current to "Korvan".

    • Since useRef does not trigger a re-render, the UI won't update when username.current is changed, but the value stored inside username will be updated.

    • console.log(username) will show the updated current value, but the displayed username in the UI will remain the same unless a re-render happens.

  3. JSX (Rendering the UI):

     <h1>Hello, {username.current}</h1>
     <button onClick={handleName}>Change UserName</button>
    
    • Initially, "Vitthal" will be displayed in the <h1> tag as username.current is "Vitthal".

    • Clicking the "Change UserName" button will change username.current to "Korvan", but the displayed name won't update because useRef does not cause a re-render.

Important Concepts

  • useRef vs. useState:

    • useState causes a component to re-render when its value is updated.

    • useRef does not cause a re-render when updated. It is mainly used for persisting values between renders or for directly interacting with DOM elements (e.g., focusing on an input).

  • No Re-render:

    • Although the username.current is updated to "Korvan", the component doesn’t re-render, so "Vitthal" will still be displayed.

    • If you want to re-render the component when the value changes, you should use useState instead of useRef.

Uncontrolled Input Using UseRef:

import React, { useRef } from "react";

export function HandleInputUseRef() {
      const usernameRef = useRef()
      const passwordRef = useRef()

      const handleSubmit = (e)=>{
            e.preventDefault()
            const usernameInput =usernameRef.current
            const passwordInput = passwordRef.current
            console.log(usernameInput.value);
            console.log(passwordInput.value);     
      }
  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="username">UserName</label>
      <br />
      <input type="text" id="username" ref={usernameRef}/>
      <br />
      <label htmlFor="password">password</label>
      <br />
      <input type="text" id="password" ref={passwordRef}/>
      <br />
      <button>Submit</button>
    </form>
  );
}

How It Works

1. Imports and useRef Hook

import React, { useRef } from "react";
  • You import React and the useRef hook. useRef is a hook in React that allows you to directly access and manipulate DOM elements or persist values without causing re-renders. In this case, it's being used to reference the input fields in the form.

2. Creating References with useRef

const usernameRef = useRef(null);
const passwordRef = useRef(null);
  • useRef(null) creates references for the username and password inputs, initially set to null because the elements don't exist in the DOM until they are rendered. Once rendered, useRef will store the reference to the input elements.

3. Form Submit Handler

const handleSubmit = (e) => {
  e.preventDefault();
  const usernameInput = usernameRef.current;
  const passwordInput = passwordRef.current;
  console.log(usernameInput.value);
  console.log(passwordInput.value);
};
  • handleSubmit is the function triggered when the form is submitted.

  • e.preventDefault() prevents the default form submission behavior (reloading the page).

  • usernameRef.current and passwordRef.current access the actual DOM elements for the username and password inputs.

  • usernameInput.value and passwordInput.value fetch the values entered by the user.

  • The values are logged to the console.

4. Form JSX Structure

<form onSubmit={handleSubmit}>
  <label htmlFor="username">UserName</label>
  <br />
  <input type="text" id="username" ref={usernameRef} />
  <br />
  <label htmlFor="password">Password</label>
  <br />
  <input type="password" id="password" ref={passwordRef} />
  <br />
  <button>Submit</button>
</form>
  • The form contains two labeled input fields: one for the username and one for the password.

  • Each input has its ref attribute set to usernameRef and passwordRef, linking the DOM element to the useRef hook.

  • The onSubmit event is tied to the handleSubmit function, so when the user submits the form, the entered values are logged to the console.

Key Points:

  • useRef: It provides a way to reference DOM elements without causing re-renders. You use it to store references to the username and password inputs.

  • Form Submission: On submission, handleSubmit accesses the current values of these inputs and logs them.

  • Direct DOM Manipulation: Unlike useState, using useRef does not cause the component to re-render when the values are changed or accessed.

GitHub Code : Working With Forms

6
Subscribe to my newsletter

Read articles from Vitthal Korvan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vitthal Korvan
Vitthal Korvan

πŸš€ Hello, World! I'm Vitthal Korvan πŸš€ As a passionate front-end web developer, I transform digital landscapes into captivating experiences. you'll find me exploring the intersection of technology and art, sipping on a cup of coffee, or contributing to the open-source community. Life is an adventure, and I bring that spirit to everything I do.