React
05 / 07

Core Concepts

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