React Fundamentals
Building modern user interfaces with React components and JSX
React Fundamentals
React is a powerful JavaScript library for building user interfaces. This lesson covers the essential concepts that form the foundation of React development: components, JSX, props, and state.
Learning Objectives
By the end of this lesson, you will understand:
- What React is and why it's popular
- How to create and use React components
- JSX syntax and how it differs from HTML
- How to pass data between components with props
- How to manage component state with useState
What is React?
React is a JavaScript library developed by Facebook for building user interfaces, particularly web applications. Key characteristics:
Component-Based Architecture
- Build encapsulated components that manage their own state
- Compose components to create complex UIs
- Reusable and maintainable code
Declarative Programming
- Describe what the UI should look like for any given state
- React handles the how (DOM manipulation)
- More predictable and easier to debug
Virtual DOM
- React maintains a virtual representation of the DOM
- Efficiently updates only what has changed
- Better performance than direct DOM manipulation
Your First React Component
Function Components
Modern React uses function components with hooks:
function Welcome() { return <h1>Hello, World!</h1>;}
Arrow Function Components
const Welcome = () => { return <h1>Hello, World!</h1>;};
Rendering Components
import React from 'react';import ReactDOM from 'react-dom/client';
function App() { return ( <div> <Welcome /> <Welcome /> </div> );}
const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<App />);
JSX (JavaScript XML)
JSX is a syntax extension that allows you to write HTML-like code in JavaScript.
JSX vs HTML Differences
// JSX uses className instead of class<div className="container"> <h1>Hello React</h1></div>
// JSX uses camelCase for attributes<input type="text" onChange={handleChange} maxLength={100}/>
// Self-closing tags must be closed<img src="photo.jpg" alt="Description" /><br /><hr />
Embedding JavaScript in JSX
function Greeting() { const name = "Alice"; const isLoggedIn = true;
return ( <div> <h1>Hello, {name}!</h1> {isLoggedIn && <p>Welcome back!</p>} <p>You have {2 + 3} unread messages</p> </div> );}
Conditional Rendering
function UserStatus({ isLoggedIn, username }) { return ( <div> {isLoggedIn ? ( <h1>Welcome back, {username}!</h1> ) : ( <h1>Please sign in</h1> )} </div> );}
Rendering Lists
function TodoList() { const todos = [ { id: 1, text: "Learn React", completed: false }, { id: 2, text: "Build a project", completed: false }, { id: 3, text: "Deploy to production", completed: true } ];
return ( <ul> {todos.map(todo => ( <li key={todo.id}> {todo.text} {todo.completed && '✓'} </li> ))} </ul> );}
Props (Properties)
Props allow you to pass data from parent to child components.
Basic Props
function Greeting({ name, age }) { return ( <div> <h1>Hello, {name}!</h1> <p>You are {age} years old</p> </div> );}
function App() { return ( <div> <Greeting name="Alice" age={30} /> <Greeting name="Bob" age={25} /> </div> );}
Props with Different Data Types
function UserCard({ name, age, isOnline, hobbies, address}) { return ( <div className="user-card"> <h2>{name}</h2> <p>Age: {age}</p> <p>Status: {isOnline ? 'Online' : 'Offline'}</p>
<h3>Hobbies:</h3> <ul> {hobbies.map((hobby, index) => ( <li key={index}>{hobby}</li> ))} </ul>
<p>Location: {address.city}, {address.country}</p> </div> );}
// Usage<UserCard name="Alice" age={28} isOnline={true} hobbies={["reading", "hiking", "coding"]} address={{ city: "New York", country: "USA" }}/>
Default Props
function Button({ text = "Click me", type = "button" }) { return <button type={type}>{text}</button>;}
// These are equivalent<Button /><Button text="Click me" type="button" />
Children Prop
function Card({ children }) { return ( <div className="card"> {children} </div> );}
function App() { return ( <Card> <h2>Card Title</h2> <p>Card content goes here</p> <button>Action</button> </Card> );}
State with useState Hook
State allows components to store and manage data that can change over time.
Basic useState
import { useState } from 'react';
function Counter() { const [count, setCount] = useState(0);
return ( <div> <p>Current count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> <button onClick={() => setCount(count - 1)}> Decrement </button> <button onClick={() => setCount(0)}> Reset </button> </div> );}
State with Objects
function UserProfile() { const [user, setUser] = useState({ name: '', email: '', age: 0 });
const updateName = (newName) => { setUser(prevUser => ({ ...prevUser, name: newName })); };
return ( <div> <input type="text" placeholder="Name" value={user.name} onChange={(e) => updateName(e.target.value)} /> <input type="email" placeholder="Email" value={user.email} onChange={(e) => setUser(prev => ({ ...prev, email: e.target.value }))} /> <p>Name: {user.name}</p> <p>Email: {user.email}</p> </div> );}
State with Arrays
function TodoApp() { const [todos, setTodos] = useState([]); const [inputValue, setInputValue] = useState('');
const addTodo = () => { if (inputValue.trim()) { setTodos(prevTodos => [ ...prevTodos, { id: Date.now(), text: inputValue, completed: false } ]); setInputValue(''); } };
const toggleTodo = (id) => { setTodos(prevTodos => prevTodos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); };
return ( <div> <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && addTodo()} /> <button onClick={addTodo}>Add Todo</button>
<ul> {todos.map(todo => ( <li key={todo.id} onClick={() => toggleTodo(todo.id)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} > {todo.text} </li> ))} </ul> </div> );}
Event Handling
React handles events using SyntheticEvents, which wrap native events.
Common Event Handlers
function EventExamples() { const [message, setMessage] = useState('');
const handleClick = () => { alert('Button clicked!'); };
const handleMouseOver = (e) => { console.log('Mouse over:', e.target); };
const handleSubmit = (e) => { e.preventDefault(); console.log('Form submitted with:', message); };
const handleKeyDown = (e) => { if (e.key === 'Enter') { console.log('Enter key pressed'); } };
return ( <form onSubmit={handleSubmit}> <button onClick={handleClick} onMouseOver={handleMouseOver} > Click me </button>
<input type="text" value={message} onChange={(e) => setMessage(e.target.value)} onKeyDown={handleKeyDown} placeholder="Type something..." />
<button type="submit">Submit</button> </form> );}
Component Composition
Building complex UIs by combining simpler components.
Composition Example
function Header() { return ( <header> <h1>My App</h1> <nav> <a href="#home">Home</a> <a href="#about">About</a> <a href="#contact">Contact</a> </nav> </header> );}
function Sidebar() { return ( <aside> <h3>Quick Links</h3> <ul> <li><a href="#link1">Link 1</a></li> <li><a href="#link2">Link 2</a></li> </ul> </aside> );}
function MainContent({ children }) { return ( <main> {children} </main> );}
function App() { return ( <div className="app"> <Header /> <div className="content"> <Sidebar /> <MainContent> <h2>Welcome to My App</h2> <p>This is the main content area.</p> </MainContent> </div> </div> );}
Best Practices
Component Organization
// ✅ Good: Clear, descriptive namesfunction UserProfileCard({ user }) { return ( <div className="user-profile-card"> <img src={user.avatar} alt={`${user.name}'s avatar`} /> <h3>{user.name}</h3> <p>{user.email}</p> </div> );}
// ❌ Avoid: Vague namesfunction Card({ data }) { return <div>{data.name}</div>;}
State Management
// ✅ Good: Keep state close to where it's usedfunction SearchComponent() { const [searchTerm, setSearchTerm] = useState(''); const [results, setResults] = useState([]);
// Component logic here}
// ❌ Avoid: Too much state in one componentfunction App() { const [searchTerm, setSearchTerm] = useState(''); const [userProfile, setUserProfile] = useState({}); const [cartItems, setCartItems] = useState([]); const [notifications, setNotifications] = useState([]); // ... too much state!}
Props Validation (TypeScript)
interface UserProps { name: string; age: number; email?: string; // Optional}
function User({ name, age, email }: UserProps) { return ( <div> <h2>{name}</h2> <p>Age: {age}</p> {email && <p>Email: {email}</p>} </div> );}
Common Patterns
Controlled Components
function ContactForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '' });
const handleChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); };
const handleSubmit = (e) => { e.preventDefault(); console.log('Submitting:', formData); };
return ( <form onSubmit={handleSubmit}> <input type="text" name="name" value={formData.name} onChange={handleChange} placeholder="Your name" /> <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="Your email" /> <textarea name="message" value={formData.message} onChange={handleChange} placeholder="Your message" /> <button type="submit">Send</button> </form> );}
Lifting State Up
function Parent() { const [sharedState, setSharedState] = useState('');
return ( <div> <ChildA value={sharedState} onChange={setSharedState} /> <ChildB value={sharedState} /> </div> );}
function ChildA({ value, onChange }) { return ( <input type="text" value={value} onChange={(e) => onChange(e.target.value)} /> );}
function ChildB({ value }) { return <p>Value from sibling: {value}</p>;}
Debugging React
React Developer Tools
- Install React DevTools browser extension
- Inspect component hierarchy
- View props and state
- Track component updates
Common Debugging Techniques
function DebuggingExample() { const [count, setCount] = useState(0);
// Log to see when component renders console.log('Component rendered with count:', count);
// Log state changes const handleIncrement = () => { console.log('Before increment:', count); setCount(prev => { const newCount = prev + 1; console.log('After increment:', newCount); return newCount; }); };
return ( <div> <p>Count: {count}</p> <button onClick={handleIncrement}>+1</button> </div> );}
Summary
React fundamentals provide the foundation for building modern web applications:
- Components: Reusable pieces of UI
- JSX: HTML-like syntax in JavaScript
- Props: Data passed between components
- State: Component data that can change
- Events: Handling user interactions
Key principles:
- Components should be focused and reusable
- Props flow down, events flow up
- State should be kept as local as possible
- Always use keys when rendering lists
Next Steps
Now that you understand React fundamentals, you're ready to explore:
- React Hooks (useEffect, useContext, custom hooks)
- Component lifecycle and side effects
- Styling React components
- React Router for navigation
- State management with Context API or external libraries
Practice building small applications to reinforce these concepts before moving on to more advanced topics!
Lesson Complete!
Great job! You've finished this lesson. Ready to continue?