React Components
Building blocks of your React application
What are Components?
Components are independent, reusable pieces of code that return React elements to be rendered to the page.
Think of components as JavaScript functions: They accept inputs (called "props") and return React elements describing what should appear on the screen.
Component Types
Functional Components
Modern approach using functions and Hooks
- Simpler syntax
- Use Hooks for state and effects
- Better performance
- Recommended approach
Class Components
Traditional ES6 class approach
- More verbose
- Uses lifecycle methods
- Legacy code support
- Being phased out
Functional Components
Basic Functional Component
// Simple functional component
function Welcome() {
return <h1>Hello, World!</h1>;
}
// Arrow function syntax
const Welcome = () => {
return <h1>Hello, World!</h1>;
};
// Shorthand (implicit return)
const Welcome = () => <h1>Hello, World!</h1>;
Component with JSX
function UserProfile() {
return (
<div className="profile">
<img src="avatar.jpg" alt="User" />
<h2>John Doe</h2>
<p>Software Developer</p>
</div>
);
}
export default UserProfile;
Exporting Components
// Default export
export default function Button() {
return <button>Click me</button>;
}
// Named export
export function PrimaryButton() {
return <button className="primary">Primary</button>;
}
export function SecondaryButton() {
return <button className="secondary">Secondary</button>;
}
// Import examples
import Button from './Button';
import { PrimaryButton, SecondaryButton } from './Buttons';
Props (Properties)
Props are arguments passed to React components, similar to function parameters.
Basic Props
// Component with props
function Greeting({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
</div>
);
}
// Usage
<Greeting name="Alice" age={25} />
Default Props
function Button({ text = "Click me", variant = "primary" }) {
return <button className={`btn btn-${variant}`}>{text}</button>;
}
// Usage
<Button /> // Uses defaults
<Button text="Submit" variant="success" />
Props with Children
function Card({ title, children }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-content">
{children}
</div>
</div>
);
}
// Usage
<Card title="My Card">
<p>This is the card content</p>
<button>Action</button>
</Card>
Props Destructuring
// Without destructuring
function User(props) {
return <p>{props.name} - {props.email}</p>;
}
// With destructuring (recommended)
function User({ name, email }) {
return <p>{name} - {email}</p>;
}
// With rest operator
function User({ name, ...otherProps }) {
return (
<div {...otherProps}>
<p>{name}</p>
</div>
);
}
State Management
State allows components to create and manage their own data.
useState Hook
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
);
}
Multiple State Variables
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
// API call here
await submitForm({ name, email });
setIsSubmitting(false);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<button disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
Object State
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
const handleChange = (e) => {
setUser({
...user,
[e.target.name]: e.target.value
});
};
return (
<form>
<input
name="name"
value={user.name}
onChange={handleChange}
/>
<input
name="email"
value={user.email}
onChange={handleChange}
/>
<input
name="age"
type="number"
value={user.age}
onChange={handleChange}
/>
</form>
);
}
Component Lifecycle
Understanding component lifecycle with useEffect Hook.
useEffect Hook
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// Runs after every render
useEffect(() => {
console.log('Component rendered');
});
// Runs only once (on mount)
useEffect(() => {
fetchUser();
}, []);
// Runs when userId changes
useEffect(() => {
fetchUser();
}, [userId]);
// Cleanup function
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
return () => clearInterval(timer);
}, []);
const fetchUser = async () => {
setLoading(true);
const data = await fetch(`/api/users/${userId}`);
setUser(await data.json());
setLoading(false);
};
if (loading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}
Best Practices
Do's
- Keep components small and focused
- Use functional components with Hooks
- Extract reusable logic into custom Hooks
- Use PropTypes or TypeScript for type checking
- Follow naming conventions (PascalCase)
- Use composition over inheritance
- Keep state as local as possible
Don'ts
- Don't mutate state directly
- Don't use indexes as keys in lists
- Don't create components inside other components
- Don't forget to add dependencies to useEffect
- Don't use class components for new code
- Don't prop drill excessively
- Don't ignore console warnings
Component Structure Example
// Good component structure
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './TodoList.css';
function TodoList({ initialTodos = [] }) {
const [todos, setTodos] = useState(initialTodos);
useEffect(() => {
// Side effects here
}, []);
const addTodo = (text) => {
setTodos([...todos, { id: Date.now(), text, completed: false }]);
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<div className="todo-list">
{/* Component JSX */}
</div>
);
}
TodoList.propTypes = {
initialTodos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number,
text: PropTypes.string,
completed: PropTypes.bool
}))
};
export default TodoList;