Components and JSX
Learning Objectives
- By the end of this lesson, you will be able to:
- - Create functional components
- - Understand class components
- - Work with props
- - Compose components
- - Build reusable components
- - Pass data between components
- - Structure React applications
Lesson 25.2: Components and Props
Learning Objectives
By the end of this lesson, you will be able to:
- Create functional components
- Understand class components
- Work with props
- Compose components
- Build reusable components
- Pass data between components
- Structure React applications
Introduction to Components
Components are the building blocks of React applications. They let you split the UI into independent, reusable pieces.
What are Components?
- Reusable: Use components multiple times
- Independent: Each component manages its own logic
- Composable: Combine components to build UIs
- Isolated: Changes in one don't affect others
Functional Components
Basic Functional Component
// Simple functional component
function Welcome() {
return <h1>Hello, World!</h1>;
}
// Arrow function component
const Welcome = () => {
return <h1>Hello, World!</h1>;
};
// Usage
function App() {
return <Welcome />;
}
Component with Props
// Component with props
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// Usage
function App() {
return <Welcome name="Alice" />;
}
// Arrow function with props
const Welcome = (props) => {
return <h1>Hello, {props.name}!</h1>;
};
Destructuring Props
// Destructure props
function Welcome({ name, age }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old</p>
</div>
);
}
// Usage
function App() {
return <Welcome name="Alice" age={30} />;
}
Class Components
Basic Class Component
import React, { Component } from 'react';
class Welcome extends Component {
render() {
return <h1>Hello, World!</h1>;
}
}
// Usage
function App() {
return <Welcome />;
}
Class Component with Props
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
// Usage
function App() {
return <Welcome name="Alice" />;
}
Note on Class Components
// Class components are still supported but
// Functional components with hooks are preferred
// Most new code uses functional components
Props
What are Props?
Props (properties) are data passed from parent to child components.
Passing Props
// Parent component
function App() {
return (
<div>
<User name="Alice" age={30} email="alice@example.com" />
<User name="Bob" age={25} email="bob@example.com" />
</div>
);
}
// Child component
function User({ name, age, email }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}
Props are Read-Only
// ❌ Bad: Don't modify props
function User({ name }) {
name = 'Changed'; // Error: Props are read-only
return <h1>{name}</h1>;
}
// ✅ Good: Use props as-is
function User({ name }) {
return <h1>{name}</h1>;
}
Default Props
// Default props
function Greeting({ name = 'Guest' }) {
return <h1>Hello, {name}!</h1>;
}
// Usage
<Greeting /> // Hello, Guest!
<Greeting name="Alice" /> // Hello, Alice!
// Or with defaultProps (class components)
class Greeting extends Component {
static defaultProps = {
name: 'Guest'
};
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
PropTypes (Type Checking)
import PropTypes from 'prop-types';
function User({ name, age, email }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
email: PropTypes.string.isRequired
};
Component Composition
Composing Components
// Small components
function Header({ title }) {
return <header><h1>{title}</h1></header>;
}
function Content({ children }) {
return <main>{children}</main>;
}
function Footer({ text }) {
return <footer><p>{text}</p></footer>;
}
// Compose into larger component
function Page() {
return (
<div>
<Header title="My Page" />
<Content>
<p>Page content goes here</p>
</Content>
<Footer text="Copyright 2024" />
</div>
);
}
Children Prop
// Component with children
function Container({ children, title }) {
return (
<div className="container">
<h2>{title}</h2>
{children}
</div>
);
}
// Usage
function App() {
return (
<Container title="My Container">
<p>This is the content</p>
<button>Click me</button>
</Container>
);
}
Component Reusability
// Reusable Button component
function Button({ text, onClick, variant = 'primary' }) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{text}
</button>
);
}
// Usage
function App() {
return (
<div>
<Button text="Submit" onClick={() => alert('Submitted')} />
<Button text="Cancel" variant="secondary" onClick={() => alert('Cancelled')} />
<Button text="Delete" variant="danger" onClick={() => alert('Deleted')} />
</div>
);
}
Practical Examples
Example 1: Card Component
function Card({ title, description, image }) {
return (
<div className="card">
{image && <img src={image} alt={title} />}
<div className="card-content">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>
);
}
// Usage
function App() {
return (
<div className="card-grid">
<Card
title="React"
description="A JavaScript library"
image="/react-logo.png"
/>
<Card
title="Vue"
description="A progressive framework"
image="/vue-logo.png"
/>
</div>
);
}
Example 2: List Component
function List({ items, renderItem }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>
{renderItem(item)}
</li>
))}
</ul>
);
}
// Usage
function App() {
let users = [
{ id: 1, name: 'Alice', age: 30 },
{ id: 2, name: 'Bob', age: 25 }
];
return (
<List
items={users}
renderItem={(user) => (
<div>
<strong>{user.name}</strong> - {user.age} years old
</div>
)}
/>
);
}
Example 3: Layout Component
function Layout({ header, sidebar, main, footer }) {
return (
<div className="layout">
<header>{header}</header>
<div className="layout-body">
<aside>{sidebar}</aside>
<main>{main}</main>
</div>
<footer>{footer}</footer>
</div>
);
}
// Usage
function App() {
return (
<Layout
header={<h1>My App</h1>}
sidebar={<nav>Navigation</nav>}
main={<div>Main content</div>}
footer={<p>Footer</p>}
/>
);
}
Practice Exercise
Exercise: Building Components
Objective: Practice creating functional components, working with props, and composing components.
Instructions:
- Create a React project
- Create multiple components
- Practice:
- Creating functional components
- Passing and using props
- Composing components
- Building reusable components
Example Solution:
// src/components/Button.jsx
import React from 'react';
function Button({ text, onClick, variant = 'primary', disabled = false }) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
}
export default Button;
// src/components/Card.jsx
import React from 'react';
function Card({ title, description, image, children }) {
return (
<div className="card">
{image && <img src={image} alt={title} className="card-image" />}
<div className="card-content">
<h3 className="card-title">{title}</h3>
{description && <p className="card-description">{description}</p>}
{children}
</div>
</div>
);
}
export default Card;
// src/components/UserCard.jsx
import React from 'react';
import Card from './Card';
import Button from './Button';
function UserCard({ user, onEdit, onDelete }) {
return (
<Card
title={user.name}
description={`Age: ${user.age} | Email: ${user.email}`}
>
<div className="card-actions">
<Button text="Edit" onClick={() => onEdit(user.id)} variant="secondary" />
<Button text="Delete" onClick={() => onDelete(user.id)} variant="danger" />
</div>
</Card>
);
}
export default UserCard;
// src/components/UserList.jsx
import React from 'react';
import UserCard from './UserCard';
function UserList({ users, onEdit, onDelete }) {
if (users.length === 0) {
return <p>No users found</p>;
}
return (
<div className="user-list">
{users.map(user => (
<UserCard
key={user.id}
user={user}
onEdit={onEdit}
onDelete={onDelete}
/>
))}
</div>
);
}
export default UserList;
// src/components/Header.jsx
import React from 'react';
function Header({ title, subtitle }) {
return (
<header className="header">
<h1>{title}</h1>
{subtitle && <p className="subtitle">{subtitle}</p>}
</header>
);
}
export default Header;
// src/components/Layout.jsx
import React from 'react';
function Layout({ header, children, footer }) {
return (
<div className="layout">
{header && <header>{header}</header>}
<main className="main-content">
{children}
</main>
{footer && <footer>{footer}</footer>}
</div>
);
}
export default Layout;
// src/App.jsx
import React from 'react';
import Layout from './components/Layout';
import Header from './components/Header';
import UserList from './components/UserList';
import Button from './components/Button';
import './App.css';
function App() {
const users = [
{ id: 1, name: 'Alice', age: 30, email: 'alice@example.com' },
{ id: 2, name: 'Bob', age: 25, email: 'bob@example.com' },
{ id: 3, name: 'Charlie', age: 35, email: 'charlie@example.com' }
];
const handleEdit = (userId) => {
console.log('Edit user:', userId);
alert(`Editing user ${userId}`);
};
const handleDelete = (userId) => {
console.log('Delete user:', userId);
if (confirm('Are you sure?')) {
alert(`Deleting user ${userId}`);
}
};
return (
<Layout
header={
<Header
title="User Management"
subtitle="Manage your users"
/>
}
footer={
<div className="footer">
<p>© 2024 My App</p>
</div>
}
>
<div className="app-content">
<div className="actions">
<Button
text="Add User"
onClick={() => alert('Add user')}
variant="primary"
/>
</div>
<UserList
users={users}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</div>
</Layout>
);
}
export default App;
/* src/App.css */
.layout {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.main-content {
flex: 1;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.header {
background-color: #282c34;
color: white;
padding: 20px;
text-align: center;
}
.header h1 {
margin: 0;
color: #61dafb;
}
.subtitle {
margin: 10px 0 0 0;
opacity: 0.8;
}
.app-content {
padding: 20px;
}
.actions {
margin-bottom: 20px;
}
.footer {
background-color: #282c34;
color: white;
padding: 10px;
text-align: center;
}
.user-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.card {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
padding: 15px;
}
.card-title {
margin: 0 0 10px 0;
color: #333;
}
.card-description {
margin: 0 0 15px 0;
color: #666;
}
.card-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
.btn-danger:hover {
background-color: #c82333;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
Expected Output (in browser):
- Layout with header and footer
- User list with cards
- Interactive buttons
- Styled components
Challenge (Optional):
- Build a complete component library
- Create complex component compositions
- Add PropTypes validation
- Build a real application
Common Mistakes
1. Modifying Props
// ❌ Bad: Modify props
function User({ name }) {
name = name.toUpperCase(); // Error
return <h1>{name}</h1>;
}
// ✅ Good: Use props as-is or create new variable
function User({ name }) {
const upperName = name.toUpperCase();
return <h1>{upperName}</h1>;
}
2. Missing Keys in Lists
// ❌ Bad: No key
{users.map(user => <UserCard user={user} />)}
// ✅ Good: With key
{users.map(user => <UserCard key={user.id} user={user} />)}
3. Not Destructuring Props
// ❌ Bad: Access via props object
function User(props) {
return <h1>{props.name}</h1>;
}
// ✅ Good: Destructure props
function User({ name }) {
return <h1>{name}</h1>;
}
Key Takeaways
- Components: Building blocks of React apps
- Functional Components: Preferred way (with hooks)
- Props: Data passed from parent to child
- Props are Read-Only: Don't modify props
- Composition: Combine components to build UIs
- Reusability: Create reusable components
- Best Practice: Destructure props, add keys, use functional components
Quiz: Components
Test your understanding with these questions:
-
Components are:
- A) Reusable
- B) Independent
- C) Both
- D) Neither
-
Props are:
- A) Read-only
- B) Mutable
- C) Both
- D) Neither
-
Functional components:
- A) Preferred
- B) Deprecated
- C) Both
- D) Neither
-
Props passed:
- A) Parent to child
- B) Child to parent
- C) Both
- D) Neither
-
children prop:
- A) Special prop
- B) Regular prop
- C) Both
- D) Neither
-
Component composition:
- A) Combining components
- B) Separating components
- C) Both
- D) Neither
-
Keys in lists:
- A) Required
- B) Optional
- C) Both
- D) Neither
Answers:
- C) Both
- A) Read-only
- A) Preferred
- A) Parent to child
- A) Special prop
- A) Combining components
- A) Required
Next Steps
Congratulations! You've learned components and props. You now know:
- How to create functional components
- How to work with props
- How to compose components
- How to build reusable components
What's Next?
- Lesson 25.3: State and Events
- Learn useState hook
- Handle events in React
- Work with controlled components
Additional Resources
- React Components: react.dev/learn/your-first-component
- Props: react.dev/learn/passing-props-to-a-component
- Component Composition: react.dev/learn/passing-data-deeply-with-context
Lesson completed! You're ready to move on to the next lesson.