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.