Published on

3 - Handling Events and Forms with TypeScript: Connecting Interactivity and Robustness in Applications

Authors
  • avatar
    Name
    Jonas de Oliveira
    Twitter

In today's web development landscape, the ability to effectively handle events and manage forms is essential for creating rich and interactive interfaces. By incorporating TypeScript into the process, you not only gain type safety but also improve code clarity and maintainability. This article explores best practices for handling events—such as onClick and onChange—and discusses input management by covering both controlled and uncontrolled components.

Handling Events: onClick, onChange, and More

Events are the lifeblood of interactivity in a React application. They allow the interface to respond to user actions, from button clicks to changes in form fields. Using TypeScript, you can precisely define event types, ensuring that your code is more predictable and safe.

Examples of Events

onClick Event

The onClick event is widely used to capture clicks on elements like buttons. Below is a simple example of a button that increments a counter when clicked:

import React, { useState } from 'react';

// Functional component that displays a counter button.
const CounterButton: React.FC = () => {
  // Initialize the "count" state variable with a number type starting at 0.
  const [count, setCount] = useState<number>(0);

  // Event handler for button click, increments the counter by 1.
  const handleClick = (): void => {
    setCount(prev => prev + 1);
  };

  return (
    <div>
      {/* Display the current counter value */}
      <p>Counter: {count}</p>
      {/* Button that triggers the handleClick function when clicked */}
      <button onClick={handleClick}>Increment</button>
    </div>
  );
};

export default CounterButton;

onChange Event

The onChange event is essential for tracking changes in input fields. It allows you to capture and store the user's input immediately in the state, making it easier to build responsive interfaces.

import React, { useState } from 'react';

// Functional component to handle text input changes.
const InputWithEvent: React.FC = () => {
  // Initialize the "text" state variable to store the current input value.
  const [text, setText] = useState<string>('');

  // Event handler for input changes, updating the state with the input's current value.
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
    setText(e.target.value);
  };

  return (
    <div>
      {/* Input field that calls handleChange whenever its value changes */}
      <input
        type="text"
        value={text}
        onChange={handleChange}
        placeholder="Type something..."
      />
      {/* Display the current text value */}
      <p>You typed: {text}</p>
    </div>
  );
};

export default InputWithEvent;

Input Management: Controlled vs. Uncontrolled Components

When it comes to forms, the approach you choose to manage inputs can significantly impact the flexibility and complexity of your code. There are two main strategies: controlled components and uncontrolled components.

Controlled Components

In controlled components, the input's value is managed by the component's state. Every change in the field is captured by an event (usually onChange) that updates the state. This approach provides complete control over form data, enabling real-time validations and dynamic behavior.

Example of a Controlled Component

import React, { useState } from 'react';

// Functional component demonstrating a controlled input.
const ControlledInput: React.FC = () => {
  // Initialize the "value" state variable to manage the input's content.
  const [value, setValue] = useState<string>('');

  // Event handler to update the state when the input value changes.
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
    setValue(e.target.value);
  };

  return (
    <div>
      {/* Input element with its value controlled by React state */}
      <input
        type="text"
        value={value}
        onChange={handleChange}
        placeholder="Controlled Component"
      />
      {/* Display the current value of the input */}
      <p>Current value: {value}</p>
    </div>
  );
};

export default ControlledInput;

Uncontrolled Components

Unlike controlled components, uncontrolled components do not rely on React's state to store their values. Instead, they use references (refs) to access the DOM elements directly. This approach is useful when you need a simpler solution or when performance is a priority in very large forms.

Example of an Uncontrolled Component

import React, { useRef } from 'react';

// Functional component demonstrating an uncontrolled input using refs.
const UncontrolledInput: React.FC = () => {
  // Create a ref to directly access the input DOM element.
  const inputRef = useRef<HTMLInputElement>(null);

  // Event handler for form submission, preventing default behavior and logging the input value.
  const handleSubmit = (e: React.FormEvent): void => {
    e.preventDefault(); // Prevent the default form submission behavior.
    // Log the current value of the input, if it exists.
    console.log('Input value:', inputRef.current?.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Input element that is not controlled by state, but accessed via a ref */}
      <input type="text" ref={inputRef} placeholder="Uncontrolled Component" />
      {/* Submit button to trigger the form submission */}
      <button type="submit">Submit</button>
    </form>
  );
};

export default UncontrolledInput;

Final Thoughts

Mastering event handling and form management with TypeScript is a crucial step for any developer who wants to create interactive, responsive, and secure interfaces. Whether you're using controlled components to ensure dynamic validations or employing refs for a simpler data capture approach, these practices demonstrate your command of modern technologies and best development practices.