Input Forms, React Lifecycle and Hooks
Table of contents
Input Forms
React provides a way to handle forms and inputs in a seamless and efficient manner. Here's an easy guide on how to handle forms and input fields in React.
Controlled Components
In React, controlled components are form elements that are controlled by the state of the component. This means that the form data is handled by the component’s state.
Handling Form Submission
Handling form submission involves preventing the default form behavior and then using the form data, typically by updating the component’s state or calling an external API.
const handleSubmit = (event) => {
event.preventDefault();
// Handle form data here
};
Handling Text Input
import React, { useState } from 'react';
function NameForm() {
const [name, setName] = useState('');
const handleChange = (event) => {
setName(event.target.value);
};
const handleSubmit = (event) => {
// Prevent page to reload after submisstion.
event.preventDefault();
alert('A name was submitted: ' + name);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={handleChange} />
</label>
<button type="submit">Submit</button>
</form>
);
}
export default NameForm;
Handling Multiple Inputs
When you have multiple input fields, you can manage them by using a single state object.
import React, { useState } from 'react';
function UserForm() {
const [user, setUser] = useState({name: "", age: ""});
const handleName = (event) => {
setUser({...user, name: event.target.value});
}
const handleAge = (event) => {
setUser({...user, age: event.target.value});
}
return (
<div>
<input type="text" value={user.name} onChange={handleName} placeholder='Enter name'/>
<input type="number" value={user.age} onChange={handleAge} placeholder='Enter age' />
<p>Name: {user.name}, Age: {user.age}</p>
</div>
);
}
export default UserForm;
Handling Textarea and Select Elements
Textarea:
import React, { useState } from 'react';
function EssayForm() {
const [essay, setEssay] = useState('');
const handleChange = (event) => {
setEssay(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
alert('An essay was submitted: ' + essay);
};
return (
<form onSubmit={handleSubmit}>
<label>
Essay:
<textarea
value={essay}
onChange={handleChange}
placeholder="Please write an essay about your favorite DOM element."
cols={30}
rows={5}
/>
</label>
<button type="submit">Submit</button>
</form>
);
}
export default EssayForm;
Select:
import React, { useState } from 'react';
function FlavorForm() {
const [flavor, setFlavor] = useState('coconut');
const handleChange = (event) => {
setFlavor(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
alert('Your favorite flavor is: ' + flavor);
};
return (
<form onSubmit={handleSubmit}>
<label>
Pick your favorite flavor:
<select value={flavor} onChange={handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
);
}
export default FlavorForm;
Uncontrolled Components
While controlled components rely on React state to manage form data as we have seen so far, uncontrolled components store their own state internally using refs.
import React, { useRef } from 'react';
function UncontrolledForm() {
const inputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
alert('A name was submitted: ' + inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={inputRef} />
</label>
<button type="submit">Submit</button>
</form>
);
}
export default UncontrolledForm;
Controlled Components: Form elements whose value is controlled by React state.
Handling Multiple Inputs: Use a single state object to manage multiple input fields.
Textarea and Select: Managed similarly to text inputs.
Form Submission: Prevent default form behavior and handle data through state or external APIs.
Controlled vs. Uncontrolled: Controlled components use React state, while uncontrolled components use refs to manage form data.
Using controlled components is the recommended way in React because it provides more control over form data and better integration with the component’s state and lifecycle.
React Lifecycle
React components have a lifecycle, which consists of several phases: mounting, updating, and unmounting. React provides lifecycle method hooks in functional components that allow you to execute code at specific points during a component’s lifecycle.
With the introduction of hooks in React 16.8, functional components, React lifecycle management is handled using hooks, primarily the useEffect
hook. The useEffect
hook can replicate the behavior of several lifecycle methods found in class components, including componentDidMount, componentDidUpdate, and componentWillUnmount.
The useEffect
Hook
The useEffect
hook takes two arguments:
A function that contains the side effect logic.
An optional array of dependencies that determine when the side effect should be run.
Syntax
import React, { useState, useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Side effect logic here
}, []); // Dependency array
}
Replicating Class Component Lifecycle Methods
componentDidMount
To replicate componentDidMount
, use useEffect
with an empty dependency array. This ensures that the effect runs only once after the initial render.
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Component mounted');
// Perform any setup work here, such as API calls
return () => {
// Cleanup work (if any)
};
}, []); // Empty dependency array
return (
<div>
<p>Component Content</p>
</div>
);
}
export default MyComponent;
componentDidUpdate
To replicate componentDidUpdate
, include variables in the dependency array that should trigger the effect when they change.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component updated with count:', count);
// Perform update-related work here
}, [count]); // Dependency array
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default MyComponent;
componentWillUnmount
To replicate componentWillUnmount
, return a cleanup function from the useEffect
hook. This function runs when the component is unmounted.
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component will unmount');
// Perform any cleanup work here, such as cancelling timers or subscriptions
};
}, []); // Empty dependency array
return (
<div>
<p>Component Content</p>
</div>
);
}
export default MyComponent;
Combining Lifecycle Methods
You can combine the behaviors of multiple lifecycle methods by including multiple useEffect
hooks or by using a single useEffect
with conditional logic.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// componentDidMount and componentWillUnmount
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component will unmount');
};
}, []);
// componentDidUpdate
useEffect(() => {
console.log('Component updated with count:', count);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default MyComponent;
Mounting (componentDidMount): Use
useEffect(() => {}, [])
.Updating (componentDidUpdate): Use
useEffect(() => {}, [dependency])
.Unmounting (componentWillUnmount): Return a cleanup function from
useEffect
.
Using useEffect
, you can handle side effects in functional components efficiently, covering all the aspects of the React component lifecycle found in class components. This makes functional components a powerful and flexible option for building React applications.
React Hooks
React hooks provide a way to use stateful logic and lifecycle features in functional components. Two essential hooks are useRef
and useEffect
.
useRef
The useRef
hook creates a mutable object that persists for the lifetime of the component. It can be used to access DOM elements directly, store a mutable value that doesn’t trigger a re-render when updated, and maintain any mutable object across renders.
To create a ref, call useRef
and pass the initial value.
import React, { useRef, useEffect } from 'react';
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input field when the component mounts
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" />
</div>
);
}
export default MyComponent;
Storing Mutable Values
Refs can store any mutable value without causing a re-render when updated.
import React, { useRef, useState } from 'react';
function Timer() {
const [count, setCount] = useState(0);
const countRef = useRef(count);
useEffect(() => {
countRef.current = count; // Keep ref updated with state
}, [count]);
const handleAlertClick = () => {
setTimeout(() => {
alert('Count: ' + countRef.current);
}, 3000);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={handleAlertClick}>Show alert in 3 seconds</button>
</div>
);
}
export default Timer;
useEffect
The useEffect
hook allows you to perform side effects in function components. As told before it combines the behavior of several lifecycle methods from class components: componentDidMount
, componentDidUpdate
, and componentWillUnmount
.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted or updated');
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default MyComponent;
Effect with Cleanup
To perform cleanup (similar to componentWillUnmount
), return a function from the effect.
import React, { useState, useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Component mounted');
return () => {
console.log('Component will unmount');
};
}, []); // Empty dependency array to run only on mount and unmount
return <div>My Component</div>;
}
export default MyComponent;
Conditional Effects
To run the effect conditionally (similar to componentDidUpdate
), pass dependencies as the second argument.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Count changed:', count);
}, [count]); // Run only when 'count' changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default MyComponent;
useRef
Creating Refs:
const ref = useRef(initialValue);
Accessing DOM Elements: Attach ref to a DOM element to directly manipulate it.
Storing Mutable Values: Use refs to store values that don’t trigger re-renders.
useEffect
Basic Side Effects: Run code after every render.
Conditional Effects: Run code only when specified dependencies change.
Cleanup: Perform cleanup by returning a function from the effect.
Combining useRef
and useEffect
import React, { useRef, useState, useEffect } from 'react';
function Stopwatch() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
useEffect(() => {
intervalRef.current = setInterval(() => {
setTime(prevTime => prevTime + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Time: {time} seconds</p>
<button onClick={() => clearInterval(intervalRef.current)}>Stop</button>
</div>
);
}
export default Stopwatch;
In this example, useEffect
starts an interval timer when the component mounts, and useRef
stores the interval ID to clear it when the component unmounts or when the user clicks the "Stop" button. This demonstrates the power of combining useRef
and useEffect
for managing side effects and mutable values in functional components.
Subscribe to my newsletter
Read articles from Syed Aquib Ali directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Syed Aquib Ali
Syed Aquib Ali
I am a MERN stack developer who has learnt everything yet trying to polish his skills 🥂, I love backend more than frontend.