A comprehensive guide to React fundamentals, hooks, patterns, and best practices.


    Table of Contents

    1. Setup & Installation
    2. Components
    3. JSX
    4. Props
    5. State
    6. Hooks
    7. Event Handling
    8. Conditional Rendering
    9. Lists & Keys
    10. Forms
    11. Lifecycle Methods (Class Components)
    12. Context API
    13. Refs
    14. Performance Optimization
    15. Error Boundaries
    16. Portals
    17. Code Splitting
    18. Common Patterns
    19. Best Practices

    Setup & Installation

    Create React App

    npx create-react-app my-app
    cd my-app
    npm start
    Bash
    npm create vite@latest my-app -- --template react
    cd my-app
    npm install
    npm run dev
    Bash

    Manual Setup with Webpack

    npm init -y
    npm install react react-dom
    npm install --save-dev webpack webpack-cli webpack-dev-server
    npm install --save-dev @babel/core @babel/preset-react babel-loader
    Bash

    Components

    Functional Component

    // Basic functional component
    function Welcome() {
      return <h1>Hello, World!</h1>;
    }
    
    // Arrow function component
    const Welcome = () => {
      return <h1>Hello, World!</h1>;
    };
    
    // Implicit return
    const Welcome = () => <h1>Hello, World!</h1>;
    
    // With props
    const Welcome = ({ name, age }) => {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          <p>Age: {age}</p>
        </div>
      );
    };
    JSX

    Class Component (Legacy)

    import React, { Component } from 'react';
    
    class Welcome extends Component {
      render() {
        return <h1>Hello, {this.props.name}!</h1>;
      }
    }
    
    // With state and lifecycle
    class Counter extends Component {
      constructor(props) {
        super(props);
        this.state = { count: 0 };
      }
    
      componentDidMount() {
        console.log('Component mounted');
      }
    
      render() {
        return (
          <div>
            <p>Count: {this.state.count}</p>
            <button onClick={() => this.setState({ count: this.state.count + 1 })}>
              Increment
            </button>
          </div>
        );
      }
    }
    JSX

    JSX

    Basic Syntax

    // JavaScript expressions in JSX
    const name = 'John';
    const element = <h1>Hello, {name}!</h1>;
    
    // Attributes (camelCase)
    const element = <div className="container" id="main"></div>;
    
    // Inline styles (object)
    const element = <div style={{ color: 'red', fontSize: '20px' }}>Text</div>;
    
    // Self-closing tags
    const element = <img src="image.jpg" alt="Description" />;
    
    // Fragments (no extra DOM node)
    const element = (
      <>
        <h1>Title</h1>
        <p>Paragraph</p>
      </>
    );
    
    // React.Fragment (with key)
    const element = (
      <React.Fragment key={item.id}>
        <h1>Title</h1>
      </React.Fragment>
    );
    JSX

    Comments in JSX

    const element = (
      <div>
        {/* Single line comment */}
        <h1>Title</h1>
        {/* 
          Multi-line
          comment
        */}
      </div>
    );
    JSX

    Props

    Passing Props

    // Parent component
    function App() {
      return <Welcome name="John" age={30} isAdmin={true} />;
    }
    
    // Child component
    function Welcome({ name, age, isAdmin }) {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          <p>Age: {age}</p>
          {isAdmin && <span>Admin User</span>}
        </div>
      );
    }
    JSX

    Default Props

    // Using default parameters
    function Welcome({ name = 'Guest', age = 0 }) {
      return <h1>Hello, {name}! Age: {age}</h1>;
    }
    
    // Using defaultProps (older pattern)
    Welcome.defaultProps = {
      name: 'Guest',
      age: 0
    };
    JSX

    Props Spreading

    const props = { name: 'John', age: 30 };
    <Welcome {...props} />
    
    // Combining with other props
    <Welcome {...props} isAdmin={true} />
    JSX

    Props Children

    // Parent
    function Card({ children }) {
      return <div className="card">{children}</div>;
    }
    
    // Usage
    <Card>
      <h1>Title</h1>
      <p>Content</p>
    </Card>
    JSX

    PropTypes (Type Checking)

    import PropTypes from 'prop-types';
    
    function Welcome({ name, age, isAdmin }) {
      return <h1>Hello, {name}!</h1>;
    }
    
    Welcome.propTypes = {
      name: PropTypes.string.isRequired,
      age: PropTypes.number,
      isAdmin: PropTypes.bool,
      callback: PropTypes.func,
      items: PropTypes.arrayOf(PropTypes.string),
      user: PropTypes.shape({
        name: PropTypes.string,
        age: PropTypes.number
      })
    };
    JSX

    State

    useState Hook

    import { useState } from 'react';
    
    function Counter() {
      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>
      );
    }
    JSX

    Multiple State Variables

    function Form() {
      const [name, setName] = useState('');
      const [age, setAge] = useState(0);
      const [isSubscribed, setIsSubscribed] = useState(false);
    
      return (
        <div>
          <input value={name} onChange={(e) => setName(e.target.value)} />
          <input value={age} onChange={(e) => setAge(Number(e.target.value))} />
          <input
            type="checkbox"
            checked={isSubscribed}
            onChange={(e) => setIsSubscribed(e.target.checked)}
          />
        </div>
      );
    }
    JSX

    State with Objects

    function UserForm() {
      const [user, setUser] = useState({
        name: '',
        email: '',
        age: 0
      });
    
      const handleChange = (e) => {
        setUser({
          ...user,
          [e.target.name]: e.target.value
        });
      };
    
      return (
        <form>
          <input name="name" value={user.name} onChange={handleChange} />
          <input name="email" value={user.email} onChange={handleChange} />
          <input name="age" value={user.age} onChange={handleChange} />
        </form>
      );
    }
    JSX

    State with Arrays

    function TodoList() {
      const [todos, setTodos] = useState([]);
    
      // Add item
      const addTodo = (text) => {
        setTodos([...todos, { id: Date.now(), text }]);
      };
    
      // Remove item
      const removeTodo = (id) => {
        setTodos(todos.filter(todo => todo.id !== id));
      };
    
      // Update item
      const updateTodo = (id, newText) => {
        setTodos(todos.map(todo => 
          todo.id === id ? { ...todo, text: newText } : todo
        ));
      };
    
      return (
        <ul>
          {todos.map(todo => (
            <li key={todo.id}>{todo.text}</li>
          ))}
        </ul>
      );
    }
    JSX

    Functional Updates

    // When new state depends on previous state
    function Counter() {
      const [count, setCount] = useState(0);
    
      const increment = () => {
        setCount(prevCount => prevCount + 1);
      };
    
      const incrementMultiple = () => {
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);
        setCount(prev => prev + 1); // Will increment by 3
      };
    
      return <button onClick={increment}>Count: {count}</button>;
    }
    JSX

    Hooks

    useState

    const [state, setState] = useState(initialValue);
    const [state, setState] = useState(() => expensiveComputation());
    JSX

    useEffect

    import { useEffect } from 'react';
    
    // Runs after every render
    useEffect(() => {
      console.log('Component rendered');
    });
    
    // Runs only on mount
    useEffect(() => {
      console.log('Component mounted');
    }, []);
    
    // Runs when dependencies change
    useEffect(() => {
      console.log('Count changed:', count);
    }, [count]);
    
    // Cleanup function
    useEffect(() => {
      const timer = setInterval(() => {
        console.log('Tick');
      }, 1000);
    
      return () => {
        clearInterval(timer);
      };
    }, []);
    
    // Multiple effects
    useEffect(() => {
      // Effect 1
    }, [dep1]);
    
    useEffect(() => {
      // Effect 2
    }, [dep2]);
    JSX

    useContext

    import { createContext, useContext } from 'react';
    
    const ThemeContext = createContext('light');
    
    function App() {
      return (
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    
    function Toolbar() {
      const theme = useContext(ThemeContext);
      return <div>Current theme: {theme}</div>;
    }
    JSX

    useReducer

    import { useReducer } from 'react';
    
    const initialState = { count: 0 };
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        case 'reset':
          return initialState;
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <div>
          <p>Count: {state.count}</p>
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
          <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
        </div>
      );
    }
    JSX

    useCallback

    import { useCallback, useState } from 'react';
    
    function Parent() {
      const [count, setCount] = useState(0);
    
      // Memoized callback
      const handleClick = useCallback(() => {
        setCount(c => c + 1);
      }, []); // Dependencies
    
      return <Child onClick={handleClick} />;
    }
    JSX

    useMemo

    import { useMemo, useState } from 'react';
    
    function ExpensiveComponent({ items }) {
      const expensiveValue = useMemo(() => {
        console.log('Computing...');
        return items.reduce((acc, item) => acc + item.value, 0);
      }, [items]);
    
      return <div>Total: {expensiveValue}</div>;
    }
    JSX

    useRef

    import { useRef, useEffect } from 'react';
    
    function TextInput() {
      const inputRef = useRef(null);
    
      useEffect(() => {
        inputRef.current.focus();
      }, []);
    
      return <input ref={inputRef} />;
    }
    
    // Storing mutable value
    function Timer() {
      const countRef = useRef(0);
    
      const handleClick = () => {
        countRef.current += 1;
        console.log('Clicked:', countRef.current);
      };
    
      return <button onClick={handleClick}>Click</button>;
    }
    JSX

    useLayoutEffect

    import { useLayoutEffect, useRef } from 'react';
    
    function Component() {
      const divRef = useRef(null);
    
      // Runs synchronously before paint
      useLayoutEffect(() => {
        const height = divRef.current.offsetHeight;
        console.log('Height:', height);
      }, []);
    
      return <div ref={divRef}>Content</div>;
    }
    JSX

    useImperativeHandle

    import { forwardRef, useImperativeHandle, useRef } from 'react';
    
    const CustomInput = forwardRef((props, ref) => {
      const inputRef = useRef();
    
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        },
        clear: () => {
          inputRef.current.value = '';
        }
      }));
    
      return <input ref={inputRef} />;
    });
    
    function Parent() {
      const inputRef = useRef();
    
      return (
        <div>
          <CustomInput ref={inputRef} />
          <button onClick={() => inputRef.current.focus()}>Focus</button>
          <button onClick={() => inputRef.current.clear()}>Clear</button>
        </div>
      );
    }
    JSX

    Custom Hooks

    // useFetch hook
    import { useState, useEffect } from 'react';
    
    function useFetch(url) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        fetch(url)
          .then(res => res.json())
          .then(data => {
            setData(data);
            setLoading(false);
          })
          .catch(err => {
            setError(err);
            setLoading(false);
          });
      }, [url]);
    
      return { data, loading, error };
    }
    
    // Usage
    function App() {
      const { data, loading, error } = useFetch('https://api.example.com/data');
    
      if (loading) return <div>Loading...</div>;
      if (error) return <div>Error: {error.message}</div>;
      return <div>{JSON.stringify(data)}</div>;
    }
    
    // useLocalStorage hook
    function useLocalStorage(key, initialValue) {
      const [value, setValue] = useState(() => {
        const item = localStorage.getItem(key);
        return item ? JSON.parse(item) : initialValue;
      });
    
      useEffect(() => {
        localStorage.setItem(key, JSON.stringify(value));
      }, [key, value]);
    
      return [value, setValue];
    }
    
    // useToggle hook
    function useToggle(initialValue = false) {
      const [value, setValue] = useState(initialValue);
      const toggle = () => setValue(v => !v);
      return [value, toggle];
    }
    
    // usePrevious hook
    function usePrevious(value) {
      const ref = useRef();
      useEffect(() => {
        ref.current = value;
      }, [value]);
      return ref.current;
    }
    JSX

    Event Handling

    Click Events

    function Button() {
      const handleClick = (e) => {
        console.log('Button clicked', e);
      };
    
      return <button onClick={handleClick}>Click Me</button>;
    }
    
    // Inline
    <button onClick={(e) => console.log('Clicked', e)}>Click</button>
    
    // With arguments
    <button onClick={() => handleClick('arg1', 'arg2')}>Click</button>
    JSX

    Form Events

    function Form() {
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form submitted');
      };
    
      const handleChange = (e) => {
        console.log('Input changed:', e.target.value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input onChange={handleChange} />
          <button type="submit">Submit</button>
        </form>
      );
    }
    JSX

    Keyboard Events

    function Input() {
      const handleKeyDown = (e) => {
        if (e.key === 'Enter') {
          console.log('Enter pressed');
        }
      };
    
      return <input onKeyDown={handleKeyDown} />;
    }
    JSX

    Mouse Events

    function Box() {
      return (
        <div
          onMouseEnter={() => console.log('Mouse entered')}
          onMouseLeave={() => console.log('Mouse left')}
          onMouseMove={(e) => console.log('Mouse moved', e.clientX, e.clientY)}
        >
          Hover over me
        </div>
      );
    }
    JSX

    Event Pooling (Legacy)

    // In React 17+, event pooling is removed
    function handleClick(e) {
      console.log(e.type); // Works fine
      setTimeout(() => {
        console.log(e.type); // Also works in React 17+
      }, 1000);
    }
    JSX

    Conditional Rendering

    If-Else with Variable

    function Greeting({ isLoggedIn }) {
      let message;
      if (isLoggedIn) {
        message = <h1>Welcome back!</h1>;
      } else {
        message = <h1>Please sign in.</h1>;
      }
      return message;
    }
    JSX

    Ternary Operator

    function Greeting({ isLoggedIn }) {
      return (
        <div>
          {isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please sign in.</h1>}
        </div>
      );
    }
    JSX

    Logical && Operator

    function Mailbox({ unreadMessages }) {
      return (
        <div>
          <h1>Hello!</h1>
          {unreadMessages.length > 0 && (
            <h2>You have {unreadMessages.length} unread messages.</h2>
          )}
        </div>
      );
    }
    JSX

    Nullish Coalescing

    function Component({ value }) {
      return <div>{value ?? 'Default value'}</div>;
    }
    JSX

    Switch Case (with IIFE)

    function Component({ status }) {
      return (
        <div>
          {(() => {
            switch (status) {
              case 'loading':
                return <Loading />;
              case 'error':
                return <Error />;
              case 'success':
                return <Success />;
              default:
                return <div>Unknown status</div>;
            }
          })()}
        </div>
      );
    }
    JSX

    Early Return

    function Component({ user }) {
      if (!user) {
        return <div>Please log in</div>;
      }
    
      if (user.role !== 'admin') {
        return <div>Access denied</div>;
      }
    
      return <div>Admin Panel</div>;
    }
    JSX

    Lists & Keys

    Basic List Rendering

    function List() {
      const items = ['Apple', 'Banana', 'Cherry'];
    
      return (
        <ul>
          {items.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      );
    }
    JSX

    List with Objects

    function UserList() {
      const users = [
        { id: 1, name: 'John', age: 30 },
        { id: 2, name: 'Jane', age: 25 },
        { id: 3, name: 'Bob', age: 35 }
      ];
    
      return (
        <ul>
          {users.map(user => (
            <li key={user.id}>
              {user.name} - {user.age}
            </li>
          ))}
        </ul>
      );
    }
    JSX

    Keys Best Practices

    // ✅ Good: Using unique IDs
    <li key={item.id}>{item.name}</li>
    
    // ❌ Bad: Using index (can cause issues with reordering)
    <li key={index}>{item.name}</li>
    
    // ✅ Good: Using compound key
    <li key={`${item.category}-${item.id}`}>{item.name}</li>
    JSX

    Filtering Lists

    function FilteredList() {
      const [filter, setFilter] = useState('');
      const items = ['Apple', 'Banana', 'Cherry', 'Date'];
    
      const filteredItems = items.filter(item =>
        item.toLowerCase().includes(filter.toLowerCase())
      );
    
      return (
        <div>
          <input value={filter} onChange={(e) => setFilter(e.target.value)} />
          <ul>
            {filteredItems.map((item, index) => (
              <li key={index}>{item}</li>
            ))}
          </ul>
        </div>
      );
    }
    JSX

    Forms

    Controlled Components

    function Form() {
      const [name, setName] = useState('');
      const [email, setEmail] = useState('');
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Submitted:', { name, email });
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="Name"
          />
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="Email"
          />
          <button type="submit">Submit</button>
        </form>
      );
    }
    JSX

    Multiple Inputs

    function Form() {
      const [formData, setFormData] = useState({
        name: '',
        email: '',
        age: ''
      });
    
      const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(prev => ({
          ...prev,
          [name]: value
        }));
      };
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Submitted:', formData);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input name="name" value={formData.name} onChange={handleChange} />
          <input name="email" value={formData.email} onChange={handleChange} />
          <input name="age" value={formData.age} onChange={handleChange} />
          <button type="submit">Submit</button>
        </form>
      );
    }
    JSX

    Checkbox

    function CheckboxForm() {
      const [isChecked, setIsChecked] = useState(false);
    
      return (
        <label>
          <input
            type="checkbox"
            checked={isChecked}
            onChange={(e) => setIsChecked(e.target.checked)}
          />
          Accept terms
        </label>
      );
    }
    JSX

    Radio Buttons

    function RadioForm() {
      const [selected, setSelected] = useState('option1');
    
      return (
        <div>
          <label>
            <input
              type="radio"
              value="option1"
              checked={selected === 'option1'}
              onChange={(e) => setSelected(e.target.value)}
            />
            Option 1
          </label>
          <label>
            <input
              type="radio"
              value="option2"
              checked={selected === 'option2'}
              onChange={(e) => setSelected(e.target.value)}
            />
            Option 2
          </label>
        </div>
      );
    }
    JSX

    Select Dropdown

    function SelectForm() {
      const [selected, setSelected] = useState('');
    
      return (
        <select value={selected} onChange={(e) => setSelected(e.target.value)}>
          <option value="">Select...</option>
          <option value="option1">Option 1</option>
          <option value="option2">Option 2</option>
          <option value="option3">Option 3</option>
        </select>
      );
    }
    JSX

    Textarea

    function TextareaForm() {
      const [text, setText] = useState('');
    
      return (
        <textarea
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="Enter text..."
        />
      );
    }
    JSX

    Uncontrolled Components (with Refs)

    function UncontrolledForm() {
      const nameRef = useRef();
      const emailRef = useRef();
    
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Name:', nameRef.current.value);
        console.log('Email:', emailRef.current.value);
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input ref={nameRef} type="text" defaultValue="John" />
          <input ref={emailRef} type="email" />
          <button type="submit">Submit</button>
        </form>
      );
    }
    JSX

    Lifecycle Methods

    Class Component Lifecycle

    class LifecycleDemo extends Component {
      constructor(props) {
        super(props);
        this.state = { count: 0 };
        console.log('1. Constructor');
      }
    
      static getDerivedStateFromProps(props, state) {
        console.log('2. getDerivedStateFromProps');
        return null; // or return new state
      }
    
      componentDidMount() {
        console.log('4. componentDidMount');
        // Fetch data, set up subscriptions
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        console.log('5. shouldComponentUpdate');
        return true; // or false to prevent re-render
      }
    
      getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log('6. getSnapshotBeforeUpdate');
        return null; // or return snapshot value
      }
    
      componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('7. componentDidUpdate');
        // Operate on DOM, make network requests
      }
    
      componentWillUnmount() {
        console.log('8. componentWillUnmount');
        // Cleanup: clear timers, cancel requests
      }
    
      render() {
        console.log('3. Render');
        return <div>Count: {this.state.count}</div>;
      }
    }
    JSX

    Functional Component Equivalent

    function LifecycleDemo() {
      // componentDidMount
      useEffect(() => {
        console.log('Component mounted');
      }, []);
    
      // componentDidUpdate
      useEffect(() => {
        console.log('Component updated');
      });
    
      // componentWillUnmount
      useEffect(() => {
        return () => {
          console.log('Component will unmount');
        };
      }, []);
    
      // Specific prop/state change
      useEffect(() => {
        console.log('Count changed');
      }, [count]);
    
      return <div>Component</div>;
    }
    JSX

    Context API

    Creating Context

    import { createContext, useContext, useState } from 'react';
    
    // Create context
    const ThemeContext = createContext();
    
    // Provider component
    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>
      );
    }
    
    // Custom hook for using context
    function useTheme() {
      const context = useContext(ThemeContext);
      if (!context) {
        throw new Error('useTheme must be used within ThemeProvider');
      }
      return context;
    }
    
    // Usage in components
    function App() {
      return (
        <ThemeProvider>
          <Toolbar />
        </ThemeProvider>
      );
    }
    
    function Toolbar() {
      const { theme, toggleTheme } = useTheme();
      return (
        <div>
          <p>Current theme: {theme}</p>
          <button onClick={toggleTheme}>Toggle Theme</button>
        </div>
      );
    }
    JSX

    Multiple Contexts

    const UserContext = createContext();
    const ThemeContext = createContext();
    
    function App() {
      return (
        <UserContext.Provider value={user}>
          <ThemeContext.Provider value={theme}>
            <Main />
          </ThemeContext.Provider>
        </UserContext.Provider>
      );
    }
    
    function Main() {
      const user = useContext(UserContext);
      const theme = useContext(ThemeContext);
      return <div>User: {user.name}, Theme: {theme}</div>;
    }
    JSX

    Refs

    useRef for DOM Elements

    function AutoFocusInput() {
      const inputRef = useRef(null);
    
      useEffect(() => {
        inputRef.current.focus();
      }, []);
    
      return <input ref={inputRef} />;
    }
    JSX

    useRef for Mutable Values

    function Timer() {
      const intervalRef = useRef(null);
      const [count, setCount] = useState(0);
    
      const startTimer = () => {
        intervalRef.current = setInterval(() => {
          setCount(c => c + 1);
        }, 1000);
      };
    
      const stopTimer = () => {
        clearInterval(intervalRef.current);
      };
    
      useEffect(() => {
        return () => clearInterval(intervalRef.current);
      }, []);
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={startTimer}>Start</button>
          <button onClick={stopTimer}>Stop</button>
        </div>
      );
    }
    JSX

    forwardRef

    const FancyButton = forwardRef((props, ref) => (
      <button ref={ref} className="fancy-button">
        {props.children}
      </button>
    ));
    
    function Parent() {
      const buttonRef = useRef();
    
      const handleClick = () => {
        buttonRef.current.focus();
      };
    
      return (
        <div>
          <FancyButton ref={buttonRef}>Click Me</FancyButton>
          <button onClick={handleClick}>Focus the button</button>
        </div>
      );
    }
    JSX

    Callback Refs

    function MeasureExample() {
      const [height, setHeight] = useState(0);
    
      const measuredRef = useCallback(node => {
        if (node !== null) {
          setHeight(node.getBoundingClientRect().height);
        }
      }, []);
    
      return (
        <div>
          <h1 ref={measuredRef}>Hello, world</h1>
          <h2>The above header is {Math.round(height)}px tall</h2>
        </div>
      );
    }
    JSX

    Performance Optimization

    React.memo

    // Prevents re-render if props haven't changed
    const ExpensiveComponent = React.memo(({ data }) => {
      console.log('Rendering...');
      return <div>{data}</div>;
    });
    
    // Custom comparison
    const Component = React.memo(
      ({ data }) => <div>{data}</div>,
      (prevProps, nextProps) => {
        return prevProps.data === nextProps.data;
      }
    );
    JSX

    useMemo

    function ExpensiveComponent({ items }) {
      // Only recomputes when items change
      const total = useMemo(() => {
        console.log('Computing total...');
        return items.reduce((sum, item) => sum + item.price, 0);
      }, [items]);
    
      return <div>Total: ${total}</div>;
    }
    JSX

    useCallback

    function Parent() {
      const [count, setCount] = useState(0);
    
      // Memoized callback - same reference across renders
      const handleClick = useCallback(() => {
        console.log('Clicked');
      }, []);
    
      return (
        <div>
          <Child onClick={handleClick} />
          <button onClick={() => setCount(count + 1)}>Count: {count}</button>
        </div>
      );
    }
    
    const Child = React.memo(({ onClick }) => {
      console.log('Child rendered');
      return <button onClick={onClick}>Click</button>;
    });
    JSX

    Code Splitting with lazy

    import { lazy, Suspense } from 'react';
    
    const LazyComponent = lazy(() => import('./LazyComponent'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <LazyComponent />
        </Suspense>
      );
    }
    JSX

    Virtualization (React Window)

    import { FixedSizeList } from 'react-window';
    
    function VirtualList() {
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
    
      return (
        <FixedSizeList
          height={400}
          itemCount={1000}
          itemSize={35}
          width="100%"
        >
          {Row}
        </FixedSizeList>
      );
    }
    JSX

    Error Boundaries

    Class Component Error Boundary

    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false, error: null };
      }
    
      static getDerivedStateFromError(error) {
        return { hasError: true };
      }
    
      componentDidCatch(error, errorInfo) {
        console.error('Error caught:', error, errorInfo);
        // Log to error reporting service
      }
    
      render() {
        if (this.state.hasError) {
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children;
      }
    }
    
    // Usage
    function App() {
      return (
        <ErrorBoundary>
          <MyComponent />
        </ErrorBoundary>
      );
    }
    JSX

    Error Boundary with Fallback UI

    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false, error: null };
      }
    
      static getDerivedStateFromError(error) {
        return { hasError: true, error };
      }
    
      componentDidCatch(error, errorInfo) {
        console.error('Error:', error);
        console.error('Error Info:', errorInfo);
      }
    
      render() {
        if (this.state.hasError) {
          return this.props.fallback || (
            <div>
              <h1>Oops! Something went wrong.</h1>
              <p>{this.state.error?.message}</p>
              <button onClick={() => this.setState({ hasError: false })}>
                Try again
              </button>
            </div>
          );
        }
    
        return this.props.children;
      }
    }
    JSX

    Portals

    Creating a Portal

    import { createPortal } from 'react-dom';
    
    function Modal({ children, isOpen }) {
      if (!isOpen) return null;
    
      return createPortal(
        <div className="modal-overlay">
          <div className="modal">
            {children}
          </div>
        </div>,
        document.getElementById('modal-root')
      );
    }
    
    // Usage
    function App() {
      const [isOpen, setIsOpen] = useState(false);
    
      return (
        <div>
          <button onClick={() => setIsOpen(true)}>Open Modal</button>
          <Modal isOpen={isOpen}>
            <h1>Modal Content</h1>
            <button onClick={() => setIsOpen(false)}>Close</button>
          </Modal>
        </div>
      );
    }
    JSX

    Portal with Event Bubbling

    function Parent() {
      const handleClick = () => {
        console.log('Clicked in parent');
      };
    
      return (
        <div onClick={handleClick}>
          <Modal />
        </div>
      );
    }
    
    function Modal() {
      // Clicks bubble to Parent even though rendered elsewhere
      return createPortal(
        <button>Click me</button>,
        document.body
      );
    }
    JSX

    Code Splitting

    Dynamic Import with lazy

    import { lazy, Suspense } from 'react';
    
    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>
      );
    }
    JSX

    Named Exports

    const MyComponent = lazy(() =>
      import('./MyComponent').then(module => ({ default: module.MyComponent }))
    );
    JSX

    Preloading

    const LazyComponent = lazy(() => import('./LazyComponent'));
    
    // Preload on hover
    function Button() {
      const handleMouseEnter = () => {
        import('./LazyComponent'); // Preload
      };
    
      return <button onMouseEnter={handleMouseEnter}>Show Component</button>;
    }
    JSX

    Common Patterns

    Higher-Order Components (HOC)

    // HOC that adds loading prop
    function withLoading(Component) {
      return function WithLoadingComponent({ isLoading, ...props }) {
        if (isLoading) return <div>Loading...</div>;
        return <Component {...props} />;
      };
    }
    
    // Usage
    const ListWithLoading = withLoading(List);
    <ListWithLoading isLoading={true} items={items} />
    JSX

    Render Props

    function DataFetcher({ url, render }) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
    
      useEffect(() => {
        fetch(url)
          .then(res => res.json())
          .then(data => {
            setData(data);
            setLoading(false);
          });
      }, [url]);
    
      return render({ data, loading });
    }
    
    // Usage
    <DataFetcher
      url="/api/users"
      render={({ data, loading }) =>
        loading ? <div>Loading...</div> : <div>{data}</div>
      }
    />
    JSX

    Compound Components

    const Tab = ({ children, isActive }) => (
      <button className={isActive ? 'active' : ''}>
        {children}
      </button>
    );
    
    const TabPanel = ({ children, isActive }) => (
      isActive ? <div>{children}</div> : null
    );
    
    function Tabs({ children }) {
      const [activeIndex, setActiveIndex] = useState(0);
    
      return (
        <div>
          <div className="tabs">
            {React.Children.map(children, (child, index) =>
              React.cloneElement(child.props.tab, {
                isActive: index === activeIndex,
                onClick: () => setActiveIndex(index)
              })
            )}
          </div>
          <div className="panels">
            {React.Children.map(children, (child, index) =>
              React.cloneElement(child, {
                isActive: index === activeIndex
              })
            )}
          </div>
        </div>
      );
    }
    
    // Usage
    <Tabs>
      <TabPanel tab={<Tab>Tab 1</Tab>}>Content 1</TabPanel>
      <TabPanel tab={<Tab>Tab 2</Tab>}>Content 2</TabPanel>
    </Tabs>
    JSX

    Container/Presentational Pattern

    // Presentational Component (dumb)
    function UserList({ users, onDelete }) {
      return (
        <ul>
          {users.map(user => (
            <li key={user.id}>
              {user.name}
              <button onClick={() => onDelete(user.id)}>Delete</button>
            </li>
          ))}
        </ul>
      );
    }
    
    // Container Component (smart)
    function UserListContainer() {
      const [users, setUsers] = useState([]);
    
      useEffect(() => {
        fetch('/api/users')
          .then(res => res.json())
          .then(setUsers);
      }, []);
    
      const handleDelete = (id) => {
        fetch(`/api/users/${id}`, { method: 'DELETE' })
          .then(() => setUsers(users.filter(u => u.id !== id)));
      };
    
      return <UserList users={users} onDelete={handleDelete} />;
    }
    JSX

    Controlled vs Uncontrolled

    // Controlled
    function ControlledInput() {
      const [value, setValue] = useState('');
      return <input value={value} onChange={(e) => setValue(e.target.value)} />;
    }
    
    // Uncontrolled
    function UncontrolledInput() {
      const inputRef = useRef();
      const handleSubmit = () => {
        console.log(inputRef.current.value);
      };
      return <input ref={inputRef} defaultValue="Initial" />;
    }
    JSX

    Best Practices

    Component Organization

    // 1. Imports
    import { useState, useEffect } from 'react';
    import PropTypes from 'prop-types';
    import { Button } from './Button';
    import './Component.css';
    
    // 2. Component definition
    function MyComponent({ title, data }) {
      // 3. State hooks
      const [count, setCount] = useState(0);
    
      // 4. Effect hooks
      useEffect(() => {
        // Side effects
      }, []);
    
      // 5. Custom hooks
      const customValue = useCustomHook();
    
      // 6. Event handlers
      const handleClick = () => {
        setCount(count + 1);
      };
    
      // 7. Render helpers
      const renderItem = (item) => <div>{item}</div>;
    
      // 8. Early returns
      if (!data) return <div>Loading...</div>;
    
      // 9. Main render
      return (
        <div>
          <h1>{title}</h1>
          <button onClick={handleClick}>Count: {count}</button>
        </div>
      );
    }
    
    // 10. PropTypes
    MyComponent.propTypes = {
      title: PropTypes.string.isRequired,
      data: PropTypes.array
    };
    
    // 11. Default props
    MyComponent.defaultProps = {
      data: []
    };
    
    // 12. Export
    export default MyComponent;
    JSX

    Naming Conventions

    // Components: PascalCase
    function UserProfile() {}
    
    // Functions/variables: camelCase
    const handleClick = () => {};
    const userName = 'John';
    
    // Constants: UPPER_SNAKE_CASE
    const MAX_COUNT = 100;
    const API_URL = 'https://api.example.com';
    
    // Custom hooks: use prefix
    function useWindowSize() {}
    function useFetch() {}
    
    // Event handlers: handle prefix
    const handleSubmit = () => {};
    const handleChange = () => {};
    
    // Boolean variables: is/has prefix
    const isLoading = true;
    const hasError = false;
    JSX

    File Structure

    src/
    ├── components/
    │   ├── common/
    │   │   ├── Button/
    │   │   │   ├── Button.jsx
    │   │   │   ├── Button.test.jsx
    │   │   │   ├── Button.module.css
    │   │   │   └── index.js
    │   │   └── Input/
    │   └── features/
    │       └── UserProfile/
    ├── hooks/
    │   ├── useAuth.js
    │   ├── useFetch.js
    │   └── useLocalStorage.js
    ├── context/
    │   ├── AuthContext.jsx
    │   └── ThemeContext.jsx
    ├── pages/
    │   ├── Home.jsx
    │   ├── About.jsx
    │   └── Contact.jsx
    ├── utils/
    │   ├── api.js
    │   ├── helpers.js
    │   └── constants.js
    ├── App.jsx
    └── index.js
    JSX

    Key Guidelines

    // ✅ Do: Destructure props
    function Component({ name, age }) {
      return <div>{name}</div>;
    }
    
    // ❌ Don't: Use props object
    function Component(props) {
      return <div>{props.name}</div>;
    }
    
    // ✅ Do: Use meaningful variable names
    const isUserLoggedIn = checkAuth();
    const userList = fetchUsers();
    
    // ❌ Don't: Use unclear abbreviations
    const usr = getU();
    const flg = chk();
    
    // ✅ Do: Keep components small and focused
    function UserCard({ user }) {
      return (
        <div>
          <UserAvatar user={user} />
          <UserInfo user={user} />
        </div>
      );
    }
    
    // ❌ Don't: Create large, multi-purpose components
    function UserCard({ user }) {
      // 500 lines of code...
    }
    
    // ✅ Do: Use composition
    function Page() {
      return (
        <Layout>
          <Header />
          <Main />
          <Footer />
        </Layout>
      );
    }
    
    // ✅ Do: Extract reusable logic to custom hooks
    function useWindowSize() {
      const [size, setSize] = useState({ width: 0, height: 0 });
      // ...
      return size;
    }
    
    // ✅ Do: Use functional updates for state
    setCount(prev => prev + 1);
    
    // ❌ Don't: Mutate state directly
    count++; // Wrong!
    setCount(count++); // Wrong!
    
    // ✅ Do: Clean up effects
    useEffect(() => {
      const timer = setInterval(() => {}, 1000);
      return () => clearInterval(timer);
    }, []);
    
    // ✅ Do: Handle loading and error states
    if (loading) return <Spinner />;
    if (error) return <Error message={error.message} />;
    return <Data data={data} />;
    JSX

    Performance Tips

    // 1. Use React.memo for expensive components
    const ExpensiveComponent = React.memo(Component);
    
    // 2. Use useMemo for expensive calculations
    const value = useMemo(() => expensiveOperation(), [deps]);
    
    // 3. Use useCallback for callbacks passed to children
    const callback = useCallback(() => {}, [deps]);
    
    // 4. Avoid inline object/array creation in render
    // ❌ Don't
    <Component style={{ margin: 10 }} />
    <Component items={[1, 2, 3]} />
    
    // ✅ Do
    const style = { margin: 10 };
    const items = [1, 2, 3];
    <Component style={style} items={items} />
    
    // 5. Use keys properly in lists
    {items.map(item => <Item key={item.id} {...item} />)}
    
    // 6. Lazy load components
    const LazyComponent = lazy(() => import('./Component'));
    
    // 7. Debounce expensive operations
    const debouncedSearch = useMemo(
      () => debounce(handleSearch, 300),
      []
    );
    JSX

    Accessibility

    // Use semantic HTML
    <button onClick={handleClick}>Click</button>
    // Not: <div onClick={handleClick}>Click</div>
    
    // Add ARIA labels
    <button aria-label="Close modal" onClick={onClose}>×</button>
    
    // Use proper form labels
    <label htmlFor="email">Email</label>
    <input id="email" type="email" />
    
    // Add alt text to images
    <img src="avatar.jpg" alt="User profile picture" />
    
    // Manage focus
    const inputRef = useRef();
    useEffect(() => {
      inputRef.current.focus();
    }, []);
    
    // Keyboard navigation
    <div
      role="button"
      tabIndex={0}
      onClick={handleClick}
      onKeyDown={(e) => e.key === 'Enter' && handleClick()}
    >
      Click me
    </div>
    JSX

    Common Hooks Patterns

    Fetch Data Pattern

    function useFetch(url) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
    
      useEffect(() => {
        let cancelled = false;
    
        setLoading(true);
        fetch(url)
          .then(res => res.json())
          .then(data => {
            if (!cancelled) {
              setData(data);
              setLoading(false);
            }
          })
          .catch(error => {
            if (!cancelled) {
              setError(error);
              setLoading(false);
            }
          });
    
        return () => {
          cancelled = true;
        };
      }, [url]);
    
      return { data, loading, error };
    }
    JSX

    Form Handling Pattern

    function useForm(initialValues) {
      const [values, setValues] = useState(initialValues);
    
      const handleChange = (e) => {
        const { name, value, type, checked } = e.target;
        setValues({
          ...values,
          [name]: type === 'checkbox' ? checked : value
        });
      };
    
      const reset = () => setValues(initialValues);
    
      return { values, handleChange, reset };
    }
    
    // Usage
    function Form() {
      const { values, handleChange, reset } = useForm({
        name: '',
        email: ''
      });
    
      return (
        <form>
          <input name="name" value={values.name} onChange={handleChange} />
          <input name="email" value={values.email} onChange={handleChange} />
          <button type="button" onClick={reset}>Reset</button>
        </form>
      );
    }
    JSX

    TypeScript with React

    Component Props

    interface Props {
      name: string;
      age?: number;
      isActive: boolean;
      onClick: () => void;
    }
    
    const Component: React.FC<Props> = ({ name, age = 0, isActive, onClick }) => {
      return <div>{name}</div>;
    };
    JSX

    useState with TypeScript

    const [count, setCount] = useState<number>(0);
    const [user, setUser] = useState<User | null>(null);
    const [items, setItems] = useState<string[]>([]);
    JSX

    Event Types

    const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
      console.log(e);
    };
    
    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      console.log(e.target.value);
    };
    
    const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
      e.preventDefault();
    };
    JSX

    This cheatsheet covers the essential React concepts, patterns, and best practices. Keep it handy for quick reference while building React applications!


    Discover more from Altgr Blog

    Subscribe to get the latest posts sent to your email.

    Leave a Reply

    Your email address will not be published. Required fields are marked *