Skip to main content

Basics

React fundamentals including JSX, components, props, and core concepts for building user interfaces.

JSX Syntax

Basic JSX

// JSX element
const element = <h1>Hello, world!</h1>;

// JSX with expressions
const name = 'John';
const element = <h1>Hello, {name}!</h1>;

// JSX with attributes
const element = (
<div className="container" id="main">
Content
</div>
);

// JSX with inline styles
const element = (
<div style={{ color: 'blue', fontSize: '16px' }}>Styled text</div>
);

JSX Rules

// Must return single parent element
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);

// Or use React Fragment
return (
<React.Fragment>
<h1>Title</h1>
<p>Content</p>
</React.Fragment>
);

// Or short syntax
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);

JSX Expressions

// JavaScript expressions in JSX
const user = { name: 'John', age: 30 };
return (
<div>
<h1>{user.name}</h1>
<p>Age: {user.age}</p>
<p>Adult: {user.age >= 18 ? 'Yes' : 'No'}</p>
</div>
);

// Function calls
const formatName = user => `${user.firstName} ${user.lastName}`;
return <h1>{formatName(user)}</h1>;

// Array methods
const numbers = [1, 2, 3, 4, 5];
return (
<ul>
{numbers.map(number => (
<li key={number}>{number}</li>
))}
</ul>
);

Components

Functional Components

// Basic functional component
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}

// Arrow function component
const Welcome = props => {
return <h1>Hello, {props.name}!</h1>;
};

// Implicit return
const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;

// Component with multiple props
function UserCard({ name, email, avatar }) {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
</div>
);
}

Class Components (Legacy)

import React, { Component } from 'react';

class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

// Class component with state
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}

increment = () => {
this.setState({ count: this.state.count + 1 });
};

render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}

Props

Basic Props

// Passing props
function App() {
return (
<div>
<Welcome name="John" />
<Welcome name="Jane" />
</div>
);
}

// Receiving props
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}

// Destructured props
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}

Default Props

// Using default parameters
function Welcome({ name = 'Guest' }) {
return <h1>Hello, {name}!</h1>;
}

// Using defaultProps (class components)
class Welcome extends Component {
static defaultProps = {
name: 'Guest',
};

render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}

Props Validation (PropTypes)

import PropTypes from 'prop-types';

function Welcome({ name, age, isActive }) {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Age: {age}</p>
<p>Status: {isActive ? 'Active' : 'Inactive'}</p>
</div>
);
}

Welcome.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
isActive: PropTypes.bool,
};

Welcome.defaultProps = {
age: 0,
isActive: false,
};

Children Props

// Component with children
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">{children}</div>
</div>
);
}

// Usage
<Card title="My Card">
<p>This is the card content</p>
<button>Click me</button>
</Card>;

State (useState Hook)

Basic State

import React, { 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>
</div>
);
}

Multiple State Variables

function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);

return (
<form>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={age}
onChange={e => setAge(Number(e.target.value))}
placeholder="Age"
/>
</form>
);
}

Object State

function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0,
});

const updateUser = (field, value) => {
setUser(prevUser => ({
...prevUser,
[field]: value,
}));
};

return (
<div>
<input
value={user.name}
onChange={e => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={e => updateUser('email', e.target.value)}
placeholder="Email"
/>
</div>
);
}

Array State

function TodoList() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');

const addTodo = () => {
if (inputValue.trim()) {
setTodos([
...todos,
{ id: Date.now(), text: inputValue, completed: false },
]);
setInputValue('');
}
};

const toggleTodo = id => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};

const deleteTodo = id => {
setTodos(todos.filter(todo => todo.id !== id));
};

return (
<div>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Add todo"
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
}}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}

Event Handling

Basic Events

function Button() {
const handleClick = () => {
console.log('Button clicked!');
};

const handleMouseOver = () => {
console.log('Mouse over button');
};

return (
<button onClick={handleClick} onMouseOver={handleMouseOver}>
Click me
</button>
);
}

Event Object

function Input() {
const handleChange = event => {
console.log('Input value:', event.target.value);
};

const handleSubmit = event => {
event.preventDefault();
console.log('Form submitted');
};

return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}

Passing Arguments

function ButtonList() {
const handleClick = (id, name) => {
console.log(`Clicked button ${id}: ${name}`);
};

const buttons = [
{ id: 1, name: 'First' },
{ id: 2, name: 'Second' },
{ id: 3, name: 'Third' },
];

return (
<div>
{buttons.map(button => (
<button
key={button.id}
onClick={() => handleClick(button.id, button.name)}
>
{button.name}
</button>
))}
</div>
);
}

Conditional Rendering

If-Else with Ternary

function Greeting({ isLoggedIn, username }) {
return (
<div>
{isLoggedIn ? <h1>Welcome back, {username}!</h1> : <h1>Please log in</h1>}
</div>
);
}

Logical AND

function Notifications({ notifications }) {
return (
<div>
<h1>Dashboard</h1>
{notifications.length > 0 && (
<div>
<h2>Notifications ({notifications.length})</h2>
<ul>
{notifications.map(notification => (
<li key={notification.id}>{notification.message}</li>
))}
</ul>
</div>
)}
</div>
);
}

Multiple Conditions

function StatusIndicator({ status }) {
const getStatusColor = () => {
switch (status) {
case 'success':
return 'green';
case 'error':
return 'red';
case 'warning':
return 'orange';
default:
return 'gray';
}
};

const getStatusMessage = () => {
switch (status) {
case 'success':
return 'Operation successful';
case 'error':
return 'An error occurred';
case 'warning':
return 'Warning: Check your input';
default:
return 'Unknown status';
}
};

return <div style={{ color: getStatusColor() }}>{getStatusMessage()}</div>;
}

Lists and Keys

Basic Lists

function ItemList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

Complex Lists

function ProductList({ products }) {
return (
<div className="product-grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price}</p>
<button>Add to Cart</button>
</div>
))}
</div>
);
}

Keys Best Practices

// ✅ Good - unique stable IDs
{
items.map(item => <Item key={item.id} data={item} />);
}

// ❌ Bad - array index (can cause issues)
{
items.map((item, index) => <Item key={index} data={item} />);
}

// ✅ Acceptable - if items don't change order
{
staticItems.map((item, index) => <Item key={index} data={item} />);
}

Component Composition

Composition vs Inheritance

// Composition pattern
function Dialog({ title, children }) {
return (
<div className="dialog">
<h1>{title}</h1>
<div className="dialog-content">{children}</div>
</div>
);
}

// Usage
<Dialog title="Confirm Action">
<p>Are you sure you want to delete this item?</p>
<button>Yes</button>
<button>No</button>
</Dialog>;

Specialized Components

function WelcomeDialog() {
return (
<Dialog title="Welcome">
<p>Thank you for visiting our app!</p>
</Dialog>
);
}

function ConfirmDialog({ message, onConfirm, onCancel }) {
return (
<Dialog title="Confirm">
<p>{message}</p>
<button onClick={onConfirm}>Confirm</button>
<button onClick={onCancel}>Cancel</button>
</Dialog>
);
}

Quick Reference

Essential Patterns

// Component with props and state
function MyComponent({ initialCount = 0 }) {
const [count, setCount] = useState(initialCount);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}

// Event handling
const handleSubmit = e => {
e.preventDefault();
// Handle form submission
};

// Conditional rendering
{
isVisible && <Component />;
}
{
condition ? <ComponentA /> : <ComponentB />;
}

// List rendering
{
items.map(item => <Item key={item.id} {...item} />);
}

Common Mistakes to Avoid

  • Forgetting keys in lists
  • Mutating state directly
  • Using array index as key for dynamic lists
  • Missing event.preventDefault() in forms
  • Not handling null/undefined values