Home / Notebooks / Frontend
Frontend
beginner

React Essentials

Essential React concepts for building modern user interfaces with components, hooks, and state management

March 10, 2024
•
Updated regularly

React Essentials

Quick reference guide for React - a JavaScript library for building user interfaces.

What is React?

React is a JavaScript library developed by Facebook for building UIs:

  • Component-Based - Build encapsulated components
  • Declarative - Design views for each state
  • Learn Once, Write Anywhere - Develop web, mobile, desktop apps
  • Virtual DOM - Efficient updates and rendering
  • Unidirectional Data Flow - Predictable state management
  • Rich Ecosystem - Thousands of libraries and tools
  • Strong Community - Wide adoption and support
  • Installation

    Create React App

    # ========== Create New Project ==========
    npx create-react-app my-app
    
    # With TypeScript
    npx create-react-app my-app --template typescript
    
    # ========== Start Development Server ==========
    cd my-app
    npm start
    
    # Open http://localhost:3000
    
    # ========== Create New Project ==========
    npm create vite@latest my-app -- --template react
    
    # With TypeScript
    npm create vite@latest my-app -- --template react-ts
    
    # ========== Install and Run ==========
    cd my-app
    npm install
    npm run dev
    

    Manual Setup

    # ========== Install React ==========
    npm install react react-dom
    
    # For TypeScript
    npm install --save-dev @types/react @types/react-dom
    

    Components

    Function Components

    // ========== Basic Function Component ==========
    function Welcome() {
      return <h1>Hello, World!</h1>;
    }
    
    // ========== With Props ==========
    function Welcome(props) {
      return <h1>Hello, {props.name}!</h1>;
    }
    
    // Usage
    <Welcome name="Alice" />
    
    // ========== With Destructuring ==========
    function Welcome({ name, age }) {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          <p>Age: {age}</p>
        </div>
      );
    }
    
    // ========== TypeScript ==========
    interface WelcomeProps {
      name: string;
      age: number;
    }
    
    function Welcome({ name, age }: WelcomeProps) {
      return (
        <div>
          <h1>Hello, {name}!</h1>
          <p>Age: {age}</p>
        </div>
      );
    }
    
    // ========== With Default Props ==========
    function Welcome({ name = 'Guest', age = 0 }) {
      return <h1>Hello, {name}!</h1>;
    }
    
    // ========== Arrow Function ==========
    const Welcome = ({ name }) => {
      return <h1>Hello, {name}!</h1>;
    };
    
    // Short form
    const Welcome = ({ name }) => <h1>Hello, {name}!</h1>;
    

    Class Components (Legacy)

    // ========== Basic Class Component ==========
    import React, { Component } from 'react';
    
    class Welcome extends Component {
      render() {
        return <h1>Hello, {this.props.name}!</h1>;
      }
    }
    
    // ========== 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>
        );
      }
    }
    
    // ========== Lifecycle Methods ==========
    class UserProfile extends Component {
      componentDidMount() {
        // Called after component is mounted
        console.log('Component mounted');
      }
      
      componentDidUpdate(prevProps, prevState) {
        // Called after component updates
        console.log('Component updated');
      }
      
      componentWillUnmount() {
        // Called before component is unmounted
        console.log('Component will unmount');
      }
      
      render() {
        return <div>Profile</div>;
      }
    }
    

    JSX

    Basic Syntax

    // ========== JavaScript Expressions ==========
    const name = 'Alice';
    const element = <h1>Hello, {name}!</h1>;
    
    const user = { name: 'Bob', age: 30 };
    const element = <h1>Hello, {user.name}!</h1>;
    
    // ========== Attributes ==========
    const element = <img src={imageUrl} alt="Description" />;
    const element = <div className="container">Content</div>;
    const element = <button onClick={handleClick}>Click me</button>;
    
    // ========== Inline Styles ==========
    const element = (
      <div style={{ color: 'red', fontSize: '20px' }}>
        Styled text
      </div>
    );
    
    // ========== Children ==========
    const element = (
      <div>
        <h1>Title</h1>
        <p>Paragraph</p>
      </div>
    );
    
    // ========== Conditional Rendering ==========
    const element = (
      <div>
        {isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
      </div>
    );
    
    // Short circuit
    const element = (
      <div>
        {isLoggedIn && <UserGreeting />}
      </div>
    );
    
    // ========== Lists ==========
    const items = ['Apple', 'Banana', 'Orange'];
    const element = (
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    );
    
    // ========== Fragments ==========
    const element = (
      <>
        <h1>Title</h1>
        <p>Paragraph</p>
      </>
    );
    
    // Or
    const element = (
      <React.Fragment>
        <h1>Title</h1>
        <p>Paragraph</p>
      </React.Fragment>
    );
    

    Hooks

    useState

    // ========== Basic State ==========
    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>
      );
    }
    
    // ========== Multiple State Variables ==========
    function Form() {
      const [name, setName] = useState('');
      const [email, setEmail] = useState('');
      const [age, setAge] = useState(0);
      
      return (
        <form>
          <input value={name} onChange={(e) => setName(e.target.value)} />
          <input value={email} onChange={(e) => setEmail(e.target.value)} />
          <input value={age} onChange={(e) => setAge(Number(e.target.value))} />
        </form>
      );
    }
    
    // ========== Object State ==========
    function UserProfile() {
      const [user, setUser] = useState({
        name: '',
        email: '',
        age: 0
      });
      
      const updateName = (name) => {
        setUser({ ...user, name });
      };
      
      const updateEmail = (email) => {
        setUser((prevUser) => ({ ...prevUser, email }));
      };
      
      return <div>{user.name}</div>;
    }
    
    // ========== Array State ==========
    function TodoList() {
      const [todos, setTodos] = useState([]);
      
      const addTodo = (text) => {
        setTodos([...todos, { id: Date.now(), text }]);
      };
      
      const removeTodo = (id) => {
        setTodos(todos.filter(todo => todo.id !== id));
      };
      
      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>
      );
    }
    
    // ========== Lazy Initialization ==========
    function ExpensiveComponent() {
      const [data, setData] = useState(() => {
        // Only runs on initial render
        return computeExpensiveValue();
      });
      
      return <div>{data}</div>;
    }
    

    useEffect

    // ========== Basic Effect ==========
    import { useEffect } from 'react';
    
    function Example() {
      useEffect(() => {
        // Runs after every render
        console.log('Component rendered');
      });
      
      return <div>Example</div>;
    }
    
    // ========== Effect with Dependencies ==========
    function User({ userId }) {
      const [user, setUser] = useState(null);
      
      useEffect(() => {
        // Runs when userId changes
        fetch(`/api/users/${userId}`)
          .then(res => res.json())
          .then(data => setUser(data));
      }, [userId]);
      
      return <div>{user?.name}</div>;
    }
    
    // ========== Effect with Cleanup ==========
    function Timer() {
      const [count, setCount] = useState(0);
      
      useEffect(() => {
        const interval = setInterval(() => {
          setCount(c => c + 1);
        }, 1000);
        
        // Cleanup function
        return () => clearInterval(interval);
      }, []);
      
      return <div>Count: {count}</div>;
    }
    
    // ========== Run Once (Component Mount) ==========
    function DataFetcher() {
      const [data, setData] = useState(null);
      
      useEffect(() => {
        // Only runs once on mount
        fetchData().then(setData);
      }, []);
      
      return <div>{data}</div>;
    }
    
    // ========== Multiple Effects ==========
    function Component() {
      useEffect(() => {
        // Effect 1: Data fetching
        fetchData();
      }, []);
      
      useEffect(() => {
        // Effect 2: Event listener
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
      }, []);
      
      useEffect(() => {
        // Effect 3: Document title
        document.title = `Count: ${count}`;
      }, [count]);
      
      return <div>Component</div>;
    }
    

    useContext

    // ========== Create Context ==========
    import { createContext, useContext, useState } from 'react';
    
    const ThemeContext = createContext();
    
    // ========== Provider Component ==========
    function ThemeProvider({ children }) {
      const [theme, setTheme] = useState('light');
      
      const toggleTheme = () => {
        setTheme(theme === 'light' ? 'dark' : 'light');
      };
      
      return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
          {children}
        </ThemeContext.Provider>
      );
    }
    
    // ========== Consumer Component ==========
    function ThemedButton() {
      const { theme, toggleTheme } = useContext(ThemeContext);
      
      return (
        <button 
          onClick={toggleTheme}
          style={{ 
            background: theme === 'light' ? '#fff' : '#333',
            color: theme === 'light' ? '#333' : '#fff'
          }}
        >
          Toggle Theme
        </button>
      );
    }
    
    // ========== App ==========
    function App() {
      return (
        <ThemeProvider>
          <ThemedButton />
        </ThemeProvider>
      );
    }
    
    // ========== Auth Context Example ==========
    const AuthContext = createContext();
    
    function AuthProvider({ children }) {
      const [user, setUser] = useState(null);
      
      const login = async (email, password) => {
        const user = await api.login(email, password);
        setUser(user);
      };
      
      const logout = () => {
        setUser(null);
      };
      
      return (
        <AuthContext.Provider value={{ user, login, logout }}>
          {children}
        </AuthContext.Provider>
      );
    }
    
    function useAuth() {
      const context = useContext(AuthContext);
      if (!context) {
        throw new Error('useAuth must be used within AuthProvider');
      }
      return context;
    }
    

    useReducer

    // ========== Basic Reducer ==========
    import { useReducer } from 'react';
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        case 'reset':
          return { count: 0 };
        default:
          throw new Error('Unknown action');
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, { count: 0 });
      
      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>
      );
    }
    
    // ========== Complex State Management ==========
    const initialState = {
      todos: [],
      filter: 'all'
    };
    
    function todoReducer(state, action) {
      switch (action.type) {
        case 'ADD_TODO':
          return {
            ...state,
            todos: [...state.todos, action.payload]
          };
        case 'REMOVE_TODO':
          return {
            ...state,
            todos: state.todos.filter(todo => todo.id !== action.payload)
          };
        case 'TOGGLE_TODO':
          return {
            ...state,
            todos: state.todos.map(todo =>
              todo.id === action.payload
                ? { ...todo, completed: !todo.completed }
                : todo
            )
          };
        case 'SET_FILTER':
          return {
            ...state,
            filter: action.payload
          };
        default:
          return state;
      }
    }
    
    function TodoApp() {
      const [state, dispatch] = useReducer(todoReducer, initialState);
      
      const addTodo = (text) => {
        dispatch({
          type: 'ADD_TODO',
          payload: { id: Date.now(), text, completed: false }
        });
      };
      
      return <div>Todo App</div>;
    }
    

    useRef

    // ========== DOM Reference ==========
    import { useRef, useEffect } from 'react';
    
    function TextInput() {
      const inputRef = useRef(null);
      
      useEffect(() => {
        // Focus input on mount
        inputRef.current.focus();
      }, []);
      
      return <input ref={inputRef} />;
    }
    
    // ========== Previous Value ==========
    function Counter() {
      const [count, setCount] = useState(0);
      const prevCountRef = useRef();
      
      useEffect(() => {
        prevCountRef.current = count;
      }, [count]);
      
      const prevCount = prevCountRef.current;
      
      return (
        <div>
          <p>Current: {count}</p>
          <p>Previous: {prevCount}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }
    
    // ========== Mutable Value ==========
    function Timer() {
      const [count, setCount] = useState(0);
      const intervalRef = useRef();
      
      const start = () => {
        if (intervalRef.current) return;
        intervalRef.current = setInterval(() => {
          setCount(c => c + 1);
        }, 1000);
      };
      
      const stop = () => {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      };
      
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={start}>Start</button>
          <button onClick={stop}>Stop</button>
        </div>
      );
    }
    

    useMemo

    // ========== Expensive Computation ==========
    import { useMemo } from 'react';
    
    function ExpensiveComponent({ items }) {
      const sortedItems = useMemo(() => {
        console.log('Sorting items...');
        return items.sort((a, b) => a.value - b.value);
      }, [items]);
      
      return (
        <ul>
          {sortedItems.map(item => (
            <li key={item.id}>{item.value}</li>
          ))}
        </ul>
      );
    }
    
    // ========== Derived State ==========
    function UserList({ users, searchQuery }) {
      const filteredUsers = useMemo(() => {
        return users.filter(user =>
          user.name.toLowerCase().includes(searchQuery.toLowerCase())
        );
      }, [users, searchQuery]);
      
      return (
        <ul>
          {filteredUsers.map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
    

    useCallback

    // ========== Prevent Unnecessary Re-renders ==========
    import { useCallback } from 'react';
    
    function Parent() {
      const [count, setCount] = useState(0);
      const [text, setText] = useState('');
      
      // Without useCallback, new function on every render
      const handleClick = useCallback(() => {
        console.log('Button clicked');
      }, []);
      
      return (
        <div>
          <input value={text} onChange={(e) => setText(e.target.value)} />
          <Child onClick={handleClick} />
        </div>
      );
    }
    
    const Child = React.memo(({ onClick }) => {
      console.log('Child rendered');
      return <button onClick={onClick}>Click me</button>;
    });
    
    // ========== With Dependencies ==========
    function SearchComponent() {
      const [query, setQuery] = useState('');
      
      const handleSearch = useCallback((value) => {
        console.log('Searching for:', value);
        api.search(query, value);
      }, [query]);
      
      return <SearchInput onSearch={handleSearch} />;
    }
    

    Custom Hooks

    // ========== useFetch Hook ==========
    function useFetch(url) {
      const [data, setData] = useState(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState(null);
      
      useEffect(() => {
        setLoading(true);
        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 Users() {
      const { data, loading, error } = useFetch('/api/users');
      
      if (loading) return <div>Loading...</div>;
      if (error) return <div>Error: {error.message}</div>;
      
      return (
        <ul>
          {data.map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
    
    // ========== 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];
    }
    
    // Usage
    function Settings() {
      const [theme, setTheme] = useLocalStorage('theme', 'light');
      
      return (
        <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
          Current theme: {theme}
        </button>
      );
    }
    
    // ========== useDebounce Hook ==========
    function useDebounce(value, delay) {
      const [debouncedValue, setDebouncedValue] = useState(value);
      
      useEffect(() => {
        const handler = setTimeout(() => {
          setDebouncedValue(value);
        }, delay);
        
        return () => clearTimeout(handler);
      }, [value, delay]);
      
      return debouncedValue;
    }
    
    // Usage
    function SearchBox() {
      const [query, setQuery] = useState('');
      const debouncedQuery = useDebounce(query, 500);
      
      useEffect(() => {
        if (debouncedQuery) {
          api.search(debouncedQuery);
        }
      }, [debouncedQuery]);
      
      return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
    }
    

    Forms

    Controlled Components

    // ========== Simple Form ==========
    function LoginForm() {
      const [email, setEmail] = useState('');
      const [password, setPassword] = useState('');
      
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Login:', { email, password });
      };
      
      return (
        <form onSubmit={handleSubmit}>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="Email"
          />
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            placeholder="Password"
          />
          <button type="submit">Login</button>
        </form>
      );
    }
    
    // ========== Complex Form ==========
    function RegistrationForm() {
      const [formData, setFormData] = useState({
        name: '',
        email: '',
        password: '',
        country: '',
        terms: false
      });
      
      const handleChange = (e) => {
        const { name, value, type, checked } = e.target;
        setFormData(prev => ({
          ...prev,
          [name]: type === 'checkbox' ? checked : value
        }));
      };
      
      const handleSubmit = (e) => {
        e.preventDefault();
        console.log('Form data:', formData);
      };
      
      return (
        <form onSubmit={handleSubmit}>
          <input
            name="name"
            value={formData.name}
            onChange={handleChange}
            placeholder="Name"
          />
          <input
            name="email"
            type="email"
            value={formData.email}
            onChange={handleChange}
            placeholder="Email"
          />
          <input
            name="password"
            type="password"
            value={formData.password}
            onChange={handleChange}
            placeholder="Password"
          />
          <select name="country" value={formData.country} onChange={handleChange}>
            <option value="">Select country</option>
            <option value="us">United States</option>
            <option value="uk">United Kingdom</option>
          </select>
          <label>
            <input
              name="terms"
              type="checkbox"
              checked={formData.terms}
              onChange={handleChange}
            />
            Accept terms
          </label>
          <button type="submit">Register</button>
        </form>
      );
    }
    

    Form Validation

    // ========== With Validation ==========
    function ValidatedForm() {
      const [email, setEmail] = useState('');
      const [password, setPassword] = useState('');
      const [errors, setErrors] = useState({});
      
      const validate = () => {
        const newErrors = {};
        
        if (!email) {
          newErrors.email = 'Email is required';
        } else if (!/\S+@\S+\.\S+/.test(email)) {
          newErrors.email = 'Email is invalid';
        }
        
        if (!password) {
          newErrors.password = 'Password is required';
        } else if (password.length < 8) {
          newErrors.password = 'Password must be at least 8 characters';
        }
        
        return newErrors;
      };
      
      const handleSubmit = (e) => {
        e.preventDefault();
        
        const validationErrors = validate();
        if (Object.keys(validationErrors).length > 0) {
          setErrors(validationErrors);
          return;
        }
        
        // Submit form
        console.log('Form submitted');
        setErrors({});
      };
      
      return (
        <form onSubmit={handleSubmit}>
          <div>
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="Email"
            />
            {errors.email && <span className="error">{errors.email}</span>}
          </div>
          <div>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              placeholder="Password"
            />
            {errors.password && <span className="error">{errors.password}</span>}
          </div>
          <button type="submit">Submit</button>
        </form>
      );
    }
    

    Performance Optimization

    React.memo

    // ========== Prevent Re-renders ==========
    const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
      console.log('Rendering ExpensiveComponent');
      return <div>{data}</div>;
    });
    
    // ========== With Custom Comparison ==========
    const UserCard = React.memo(
      function UserCard({ user }) {
        return <div>{user.name}</div>;
      },
      (prevProps, nextProps) => {
        // Return true if props are equal (skip re-render)
        return prevProps.user.id === nextProps.user.id;
      }
    );
    

    Code Splitting

    // ========== Lazy Loading ==========
    import { lazy, Suspense } from 'react';
    
    const HeavyComponent = lazy(() => import('./HeavyComponent'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <HeavyComponent />
        </Suspense>
      );
    }
    
    // ========== Route-based Splitting ==========
    const Home = lazy(() => import('./pages/Home'));
    const About = lazy(() => import('./pages/About'));
    const Contact = lazy(() => import('./pages/Contact'));
    
    function App() {
      return (
        <Suspense fallback={<LoadingSpinner />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/contact" element={<Contact />} />
          </Routes>
        </Suspense>
      );
    }
    

    Error Handling

    Error Boundaries

    // ========== Error Boundary Component ==========
    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:', error, errorInfo);
        // Log to error reporting service
      }
      
      render() {
        if (this.state.hasError) {
          return (
            <div>
              <h1>Something went wrong</h1>
              <p>{this.state.error?.message}</p>
            </div>
          );
        }
        
        return this.props.children;
      }
    }
    
    // Usage
    function App() {
      return (
        <ErrorBoundary>
          <MyComponent />
        </ErrorBoundary>
      );
    }
    

    Common Patterns

    Container/Presentational Pattern

    // ========== Presentational Component ==========
    function UserList({ users, onUserClick }) {
      return (
        <ul>
          {users.map(user => (
            <li key={user.id} onClick={() => onUserClick(user)}>
              {user.name}
            </li>
          ))}
        </ul>
      );
    }
    
    // ========== Container Component ==========
    function UserListContainer() {
      const [users, setUsers] = useState([]);
      const [loading, setLoading] = useState(true);
      
      useEffect(() => {
        fetchUsers().then(data => {
          setUsers(data);
          setLoading(false);
        });
      }, []);
      
      const handleUserClick = (user) => {
        console.log('User clicked:', user);
      };
      
      if (loading) return <div>Loading...</div>;
      
      return <UserList users={users} onUserClick={handleUserClick} />;
    }
    

    Render Props

    // ========== Render Prop Component ==========
    function MouseTracker({ render }) {
      const [position, setPosition] = useState({ x: 0, y: 0 });
      
      useEffect(() => {
        const handleMouseMove = (e) => {
          setPosition({ x: e.clientX, y: e.clientY });
        };
        
        window.addEventListener('mousemove', handleMouseMove);
        return () => window.removeEventListener('mousemove', handleMouseMove);
      }, []);
      
      return render(position);
    }
    
    // Usage
    function App() {
      return (
        <MouseTracker
          render={({ x, y }) => (
            <div>Mouse position: {x}, {y}</div>
          )}
        />
      );
    }
    

    Higher-Order Components (HOC)

    // ========== HOC ==========
    function withLoading(Component) {
      return function WithLoadingComponent({ isLoading, ...props }) {
        if (isLoading) {
          return <div>Loading...</div>;
        }
        return <Component {...props} />;
      };
    }
    
    // Usage
    const UserListWithLoading = withLoading(UserList);
    
    function App() {
      const [isLoading, setIsLoading] = useState(true);
      const [users, setUsers] = useState([]);
      
      return <UserListWithLoading isLoading={isLoading} users={users} />;
    }
    

    Best Practices

  • Keep components small and focused - Single responsibility
  • Use functional components - Prefer hooks over class components
  • Destructure props - Cleaner and more readable
  • Use TypeScript - Type safety and better tooling
  • Avoid inline functions in JSX - Use useCallback
  • Use keys properly - Unique and stable keys for lists
  • Don't mutate state - Always create new objects/arrays
  • Use fragments - Avoid unnecessary DOM nodes
  • Handle errors - Use error boundaries
  • Optimize performance - Use React.memo, useMemo, useCallback
  • TypeScript with React

    // ========== Component Props ==========
    interface ButtonProps {
      text: string;
      onClick: () => void;
      disabled?: boolean;
    }
    
    function Button({ text, onClick, disabled = false }: ButtonProps) {
      return (
        <button onClick={onClick} disabled={disabled}>
          {text}
        </button>
      );
    }
    
    // ========== With Children ==========
    interface CardProps {
      title: string;
      children: React.ReactNode;
    }
    
    function Card({ title, children }: CardProps) {
      return (
        <div>
          <h2>{title}</h2>
          {children}
        </div>
      );
    }
    
    // ========== Event Handlers ==========
    function Form() {
      const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
      };
      
      const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        console.log(e.target.value);
      };
      
      return (
        <form onSubmit={handleSubmit}>
          <input onChange={handleChange} />
        </form>
      );
    }
    
    // ========== Generic Components ==========
    interface ListProps<T> {
      items: T[];
      renderItem: (item: T) => React.ReactNode;
    }
    
    function List<T>({ items, renderItem }: ListProps<T>) {
      return (
        <ul>
          {items.map((item, index) => (
            <li key={index}>{renderItem(item)}</li>
          ))}
        </ul>
      );
    }
    

    Resources

  • React Documentation
  • React Beta Docs
  • React TypeScript Cheatsheet
  • Awesome React
  • React Patterns
  • Topics

    ReactJavaScriptTypeScriptHooksComponents

    Found This Helpful?

    If you have questions or suggestions for improving these notes, I'd love to hear from you.