React Concepts

Learn React fundamentals, hooks, state management, and component architecture.

Concept Guide

React Fundamentals

React is a JavaScript library for building user interfaces. It's component-based, declarative, and efficient.

Key Concepts

  • Components: Reusable UI pieces that can be composed together
  • Props: Data passed down from parent to child components
  • State: Internal data that can change over time
  • JSX: Syntax extension that looks like HTML but is JavaScript

Component Structure

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

Mermaid Component Diagram

Loading diagram...

Learning Path

  1. Start with Components - Learn the building blocks
  2. Master State & Props - Understand data flow
  3. Explore Hooks - Modern React patterns
  4. Practice Data Fetching - Real-world scenarios
  5. Build Complex UIs - Advanced patterns

Why React?

  • Declarative: Describe what you want, not how to do it
  • Component-Based: Reusable, maintainable code
  • Virtual DOM: Efficient updates and rendering
  • Rich Ecosystem: Huge community and libraries
  • Learn Once, Write Anywhere: Web, mobile, desktop

Component Types

Functional Components

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

Class Components

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

Arrow Function Components

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

Props and State

Props (Read-only)

function UserCard({ user, onEdit }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <button onClick={() => onEdit(user.id)}>Edit</button>
    </div>
  );
}

State (Mutable)

function Counter() {
  const [count, setCount] = useState(0);

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

JSX Rules

  1. Must return a single element (or Fragment)
  2. Use camelCase for attributes
  3. Use className instead of class
  4. JavaScript expressions go in curly braces {}
  5. Close all tags (including self-closing)
function Example() {
  const name = 'React';
  const isActive = true;

  return (
    <div className="container">
      <h1>Welcome to {name}</h1>
      <button disabled={!isActive}>{isActive ? 'Click me' : 'Disabled'}</button>
    </div>
  );
}

Event Handling

function Form() {
  const [input, setInput] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', input);
  };

  const handleChange = (e) => {
    setInput(e.target.value);
  };

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

Conditional Rendering

function Greeting({ user }) {
  if (!user) {
    return <div>Please log in</div>;
  }

  return (
    <div>
      <h1>Welcome back, {user.name}!</h1>
      {user.isAdmin && <AdminPanel />}
    </div>
  );
}

Lists and Keys

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Component Composition

function App() {
  return (
    <div>
      <Header />
      <Main>
        <Sidebar />
        <Content />
      </Main>
      <Footer />
    </div>
  );
}

Interactive Example

React Concepts Overview

Components

Learn about functional and class components, props, and composition.

Hooks

Master useState, useEffect, and custom hooks for state management.

Data Fetching

Handle API calls, loading states, and error handling.

State Management

Explore local state, context, and advanced patterns.

Further Exploration

Advanced React Patterns

Custom Hooks

Custom hooks allow you to extract component logic into reusable functions.

function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  const reset = () => setCount(initialValue);

  return { count, increment, decrement, reset };
}

Performance Optimization

  • React.memo: Prevents unnecessary re-renders
  • useMemo: Memoizes expensive calculations
  • useCallback: Memoizes function references

Component Composition

Loading diagram...

Advanced Patterns

Render Props

function DataFetcher({ render }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData().then(setData);
  }, []);

  return render(data);
}

// Usage
<DataFetcher render={(data) => <UserList users={data} />} />;

Higher-Order Components

function withLoading(Component) {
  return function WrappedComponent(props) {
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      // Simulate loading
      setTimeout(() => setLoading(false), 1000);
    }, []);

    if (loading) return <div>Loading...</div>;
    return <Component {...props} />;
  };
}

const UserListWithLoading = withLoading(UserList);

Compound Components

function Tabs({ children }) {
  const [activeTab, setActiveTab] = useState(0);

  return (
    <TabsContext.Provider value={{ activeTab, setActiveTab }}>{children}</TabsContext.Provider>
  );
}

Tabs.List = function TabsList({ children }) {
  return <div className="tabs-list">{children}</div>;
};

Tabs.Tab = function Tab({ children, index }) {
  const { activeTab, setActiveTab } = useContext(TabsContext);

  return (
    <button className={activeTab === index ? 'active' : ''} onClick={() => setActiveTab(index)}>
      {children}
    </button>
  );
};

Tabs.Panel = function TabPanel({ children, index }) {
  const { activeTab } = useContext(TabsContext);

  if (activeTab !== index) return null;
  return <div className="tab-panel">{children}</div>;
};

// Usage
<Tabs>
  <Tabs.List>
    <Tabs.Tab index={0}>Tab 1</Tabs.Tab>
    <Tabs.Tab index={1}>Tab 2</Tabs.Tab>
  </Tabs.List>
  <Tabs.Panel index={0}>Content 1</Tabs.Panel>
  <Tabs.Panel index={1}>Content 2</Tabs.Panel>
</Tabs>;

Context API

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>;
}

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

Error Boundaries

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>Something went wrong</h2>
          <details>
            <summary>Error details</summary>
            <pre>{this.state.error?.toString()}</pre>
          </details>
        </div>
      );
    }

    return this.props.children;
  }
}

Testing React Components

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('counter increments when button is clicked', () => {
  render(<Counter />);

  const button = screen.getByRole('button');
  userEvent.click(button);

  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

test('renders user name', () => {
  const user = { name: 'John Doe' };
  render(<UserProfile user={user} />);

  expect(screen.getByText('John Doe')).toBeInTheDocument();
});

React DevTools

Profiler

import { Profiler } from 'react';

function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
  console.log(`Component ${id} took ${actualDuration}ms to render`);
}

<Profiler id="App" onRender={onRenderCallback}>
  <App />
</Profiler>;

Strict Mode

import { StrictMode } from 'react';

<StrictMode>
  <App />
</StrictMode>;

Best Practices

  1. Keep components small and focused
  2. Use TypeScript for better type safety
  3. Implement proper error boundaries
  4. Optimize for performance early
  5. Write comprehensive tests
  6. Follow consistent naming conventions

Code Splitting

Dynamic Imports

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

Route-based Splitting

import { lazy } from 'react';
import { Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </Suspense>
  );
}

Performance Monitoring

React Profiler

import { Profiler } from 'react';

function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
  // Log performance metrics
  console.log({
    id,
    phase,
    actualDuration,
    baseDuration,
    startTime,
    commitTime,
  });
}

<Profiler id="App" onRender={onRenderCallback}>
  <App />
</Profiler>;

Bundle Analysis

# Analyze bundle size
npm install --save-dev webpack-bundle-analyzer

# Add to webpack config
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

Accessibility

ARIA Labels

function SearchInput({ onSearch }) {
  return (
    <div>
      <label htmlFor="search">Search:</label>
      <input
        id="search"
        type="text"
        aria-describedby="search-help"
        placeholder="Enter search terms..."
      />
      <div id="search-help">Type keywords to find relevant content</div>
    </div>
  );
}

Keyboard Navigation

function MenuItem({ children, onSelect }) {
  const handleKeyDown = (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      onSelect();
    }
  };

  return (
    <div role="menuitem" tabIndex={0} onKeyDown={handleKeyDown} onClick={onSelect}>
      {children}
    </div>
  );
}