React Core Concepts
Master the fundamental concepts that power React - the foundation you need to build modern, efficient web applications:
What is React?
React is a JavaScript library for building user interfaces. Created by Facebook (now Meta) in 2013, it revolutionized front-end development with its component-based architecture and efficient rendering through the Virtual DOM.
Components - Building Blocks of React
Components are independent, reusable pieces of UI. They accept inputs (props) and return React elements describing what should appear on screen.
Functional Components
Functional components are JavaScript functions that return JSX. They are the modern, preferred way to write React components.
// Simple functional component
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Component with multiple elements
function UserCard({ user }) {
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>Joined: {new Date(user.joinedAt).toLocaleDateString()}</p>
</div>
);
}
// Component with conditional rendering
function Greeting({ user }) {
if (!user) {
return <h1>Welcome, Guest!</h1>;
}
return (
<div>
<h1>Welcome back, {user.name}!</h1>
<p>Last login: {user.lastLogin}</p>
</div>
);
}Class Components (Legacy)
Class components were the original way to write stateful components. While functional components with hooks are now preferred, you may encounter class components in legacy code.
import { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
componentDidMount() {
console.log('Component mounted');
}
componentWillUnmount() {
console.log('Component will unmount');
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}JSX (JavaScript XML)
JSX is a syntax extension that allows you to write HTML-like code in JavaScript. It gets compiled to React.createElement() calls, making your code more readable and maintainable.
JSX Basics
// JSX
const element = <h1>Hello, {name}!</h1>;
// Compiled to:
const element = React.createElement('h1', null, 'Hello, ', name, '!');
// JSX with attributes
const link = <a href="https://react.dev" target="_blank">React Docs</a>;
// JSX with expressions
const count = 5;
const message = <p>You have {count} new {count === 1 ? 'message' : 'messages'}</p>;
// JSX with inline styles
const box = (
<div style={{ backgroundColor: 'blue', padding: '10px', color: 'white' }}>
Styled Box
</div>
);JSX Rules
Return a single root element (or use Fragment)
Close all tags (including self-closing like <img />)
Use camelCase for attributes (className instead of class)
// ❌ Wrong - multiple root elements
function Wrong() {
return (
<h1>Title</h1>
<p>Content</p>
);
}
// ✅ Correct - single root element
function Correct() {
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
}
// ✅ Correct - using Fragment
function AlsoCorrect() {
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
}Props - Component Configuration
Props (properties) are read-only data passed from parent to child components. They allow components to be configurable and reusable.
Basic Props
// Parent component
function App() {
return <Greeting name="John" age={25} isAdmin={true} />;
}
// Child component with destructured props
function Greeting({ name, age, isAdmin }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>You are {age} years old.</p>
{isAdmin && <span className="badge">Admin</span>}
</div>
);
}Default Props & PropTypes
// Default props with ES6 default parameters
function Button({ text = 'Click me', variant = 'primary', onClick }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{text}
</button>
);
}
// With TypeScript
interface ButtonProps {
text?: string;
variant?: 'primary' | 'secondary' | 'danger';
onClick?: () => void;
disabled?: boolean;
}
function TypedButton({
text = 'Click me',
variant = 'primary',
onClick,
disabled = false,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
}Children Prop
// children prop allows nesting content
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">{children}</div>
</div>
);
}
// Usage
function App() {
return (
<Card title="User Profile">
<p>Name: John Doe</p>
<p>Email: john@example.com</p>
<button>Edit Profile</button>
</Card>
);
}State - Managing Component Data
State is data that can change over time within a component. When state changes, React re-renders the component to reflect the new data. State is local to the component and cannot be accessed by other components unless passed as props.
useState Hook
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// State with object
function UserForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
age: 0,
});
const handleChange = (field, value) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
return (
<form>
<input
value={formData.name}
onChange={(e) => handleChange('name', e.target.value)}
placeholder="Name"
/>
<input
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
placeholder="Email"
type="email"
/>
<input
value={formData.age}
onChange={(e) => handleChange('age', parseInt(e.target.value))}
placeholder="Age"
type="number"
/>
</form>
);
}State vs Props
Props: Read-only, passed from parent, used to configure component
State: Mutable, managed by component, changes trigger re-renders
Props flow down, events flow up
Virtual DOM - React's Secret Weapon
The Virtual DOM is a lightweight JavaScript representation of the real DOM. React uses it to optimize rendering by calculating the minimal number of changes needed and batching DOM updates.
How it Works
When state changes, React creates a new Virtual DOM tree
React compares (diffs) the new tree with the previous tree
React calculates the minimal set of changes needed
React batches and applies those changes to the real DOM
Reconciliation Process
Reconciliation is the process by which React updates the DOM. When a component's state or props change, React uses a diffing algorithm to determine what changed.
// Example: List rendering with keys
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
// Key helps React identify which items changed
<li key={todo.id}>
{todo.text}
</li>
))}
</ul>
);
}
// ❌ Bad - using index as key (can cause bugs)
function BadList({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li> // Don't do this!
))}
</ul>
);
}
// ✅ Good - using unique ID
function GoodList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}Component Lifecycle
React components go through different phases during their lifetime. In functional components, we manage lifecycle with useEffect hook.
Lifecycle Phases
Mounting: Component is created and inserted into the DOM
Updating: Component re-renders due to state or props changes
Unmounting: Component is removed from the DOM
Lifecycle with useEffect
import { useState, useEffect } from 'react';
function DataFetcher({ userId }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// ComponentDidMount + ComponentDidUpdate equivalent
useEffect(() => {
console.log('Effect running - component mounted or userId changed');
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
// ComponentWillUnmount equivalent (cleanup)
return () => {
console.log('Cleanup - component will unmount or before next effect');
};
}, [userId]); // Dependencies - effect runs when userId changes
if (loading) return <div>Loading...</div>;
return <div>{data?.name}</div>;
}Events and Event Handling
React handles events using camelCase syntax and passes synthetic event objects for cross-browser compatibility.
function EventExamples() {
const [text, setText] = useState('');
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
// Click handler
const handleClick = (e) => {
e.preventDefault();
console.log('Button clicked');
};
// Input change handler
const handleChange = (e) => {
setText(e.target.value);
};
// Mouse move handler
const handleMouseMove = (e) => {
setMousePos({ x: e.clientX, y: e.clientY });
};
// Form submit handler
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted with:', text);
};
return (
<div onMouseMove={handleMouseMove}>
<button onClick={handleClick}>Click Me</button>
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={handleChange}
placeholder="Type something"
/>
<button type="submit">Submit</button>
</form>
<p>Mouse: {mousePos.x}, {mousePos.y}</p>
</div>
);
}Conditional Rendering
React provides multiple ways to conditionally render components based on state or props.
// If-else with return
function UserGreeting({ user }) {
if (!user) {
return <h1>Please sign in</h1>;
}
return <h1>Welcome, {user.name}!</h1>;
}
// Ternary operator
function Status({ isOnline }) {
return (
<div>
Status: {isOnline ? '🟢 Online' : '🔴 Offline'}
</div>
);
}
// Logical && operator
function Notifications({ count }) {
return (
<div>
<h2>Notifications</h2>
{count > 0 && (
<span className="badge">{count} new</span>
)}
</div>
);
}
// Switch-case with object mapping
function StatusIcon({ status }) {
const icons = {
success: '✅',
warning: '⚠️',
error: '❌',
info: 'ℹ️',
};
return <span>{icons[status] || '❓'}</span>;
}Lists and Keys
When rendering lists, React needs keys to identify which items have changed, been added, or removed. Keys should be stable, unique among siblings, and not change between renders.
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
);
}
// Complex list with nested data
function PostList({ posts }) {
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<div className="comments">
{post.comments.map(comment => (
<div key={comment.id}>
<strong>{comment.author}:</strong> {comment.text}
</div>
))}
</div>
</article>
))}
</div>
);
}Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free