React Basics
React is a JavaScript library for building user interfaces. It's what we use to create interactive, dynamic web applications.
What is React?
React allows you to build UIs using components - reusable pieces of code that represent parts of your interface. Think of components like LEGO blocks that you can combine to build complex applications.
Why React?
- Component-based - Build encapsulated components that manage their own state
- Declarative - Describe what you want, React handles the updates
- Learn once, write anywhere - Use React for web, mobile (React Native), and more
- Large ecosystem - Tons of libraries and community support
Your First Component
function Welcome() {
return <h1>Hello, World!</h1>;
}
That's it! A component is just a function that returns JSX (HTML-like syntax).
JSX - JavaScript + XML
JSX looks like HTML but it's actually JavaScript:
function Greeting() {
const name = "Alice";
const isStudent = true;
return (
<div>
<h1>Hello, {name}!</h1>
{isStudent && <p>Welcome to the team!</p>}
</div>
);
}
JSX Rules
- Return a single parent element (or use fragments)
// Good
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
// Also good - using fragment
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
- Close all tags
<img src="photo.jpg" /> // Self-closing
<input type="text" /> // Must close
- Use
classNameinstead ofclass
<div className="container">Content</div>
- Use camelCase for attributes
<button onClick={handleClick}>Click me</button>
<div backgroundColor="blue">Content</div>
Props - Passing Data to Components
Props (properties) are how you pass data from parent to child components:
// Define component with props
interface ButtonProps {
text: string;
color: string;
}
function Button({ text, color }: ButtonProps) {
return (
<button style={{ backgroundColor: color }}>
{text}
</button>
);
}
// Use the component
function App() {
return (
<div>
<Button text="Click me" color="blue" />
<Button text="Submit" color="green" />
</div>
);
}
Props are Read-Only
You cannot modify props inside a component. They flow one way: from parent to child.
State - Managing Dynamic Data
State allows components to remember information and update the UI when it changes:
import { useState } from 'react';
function Counter() {
// Declare state variable
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>
);
}
useState Explained
const [value, setValue] = useState(initialValue);
// ↑ ↑ ↑
// current function to initial value
// value update value
Multiple State Variables
function Form() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [age, setAge] = useState(0);
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="number"
value={age}
onChange={(e) => setAge(Number(e.target.value))}
/>
</form>
);
}
Event Handling
React uses camelCase event handlers:
function EventExamples() {
const handleClick = () => {
console.log("Button clicked!");
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); // Prevent page reload
console.log("Form submitted!");
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log("Input value:", e.target.value);
};
return (
<div>
<button onClick={handleClick}>Click me</button>
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button type="submit">Submit</button>
</form>
</div>
);
}
Conditional Rendering
Show different content based on conditions:
function Greeting({ isLoggedIn }: { isLoggedIn: boolean }) {
// Using if statement
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in.</h1>;
}
function Status({ isLoading }: { isLoading: boolean }) {
// Using ternary operator
return (
<div>
{isLoading ? <p>Loading...</p> : <p>Ready!</p>}
</div>
);
}
function Message({ error }: { error: string | null }) {
// Using && operator
return (
<div>
{error && <p className="error">{error}</p>}
</div>
);
}
Lists and Keys
Render multiple items from an array:
function TodoList() {
const todos = [
{ id: 1, text: "Learn React", completed: true },
{ id: 2, text: "Build project", completed: false },
{ id: 3, text: "Deploy app", completed: false }
];
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text} {todo.completed && "✓"}
</li>
))}
</ul>
);
}
Important: Always provide a unique key prop when rendering lists!
useEffect - Side Effects
Use useEffect for things like fetching data, subscriptions, or manually changing the DOM:
import { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// This runs after component mounts
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []); // Empty array means run once on mount
if (loading) return <p>Loading...</p>;
return <div>{/* Render data */}</div>;
}
useEffect Dependencies
useEffect(() => {
// Runs once on mount
}, []);
useEffect(() => {
// Runs on every render
});
useEffect(() => {
// Runs when 'count' changes
}, [count]);
Component Composition
Build complex UIs by composing components:
function Header() {
return <header><h1>My App</h1></header>;
}
function Sidebar() {
return <aside>Sidebar content</aside>;
}
function MainContent() {
return <main>Main content</main>;
}
function App() {
return (
<div className="app">
<Header />
<div className="container">
<Sidebar />
<MainContent />
</div>
</div>
);
}
Common Patterns
Form Handling
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Submit logic here
console.log({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
Loading States
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>User not found</div>;
return <div>Welcome, {user.name}!</div>;
}
File Structure
Typical React component file:
// UserCard.tsx
import { useState } from 'react';
import './UserCard.css'; // Import styles
// Type definitions
interface UserCardProps {
name: string;
email: string;
onDelete?: () => void;
}
// Component
export function UserCard({ name, email, onDelete }: UserCardProps) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className="user-card">
<h3>{name}</h3>
<p>{email}</p>
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Show less' : 'Show more'}
</button>
{onDelete && (
<button onClick={onDelete}>Delete</button>
)}
</div>
);
}
React Best Practices
- Keep components small - One component = one responsibility
- Name components clearly -
UserProfilenotComponent1 - Extract reusable logic - Use custom hooks
- Avoid prop drilling - Use Context API or state management
- Type your props - Use TypeScript interfaces
- Handle loading and error states - Always consider all states
- Don't mutate state directly - Always use setState
- Use keys in lists - Helps React optimize rendering
Common Mistakes
-
Modifying state directly:
state.count++❌- Use:
setState(state.count + 1)✅
- Use:
-
Forgetting dependencies in useEffect
-
Not handling loading/error states
-
Props drilling too deep (use Context instead)
-
Too many useEffects in one component
Practice Exercise
Build a simple Todo App with:
- A form to add new todos
- A list displaying todos
- Ability to mark todos as complete
- Ability to delete todos
- Show count of completed vs total todos
Learn More
- React Official Tutorial
- React TypeScript Cheatsheet
- React Patterns
- Awesome React - Curated list of React resources
Next Up
Excellent! You now know the fundamentals. Let's learn our team's workflow for creating features and contributing code!