Suggest an editImprove this articleRefine the answer for “component lifecycle methods in React”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**React component lifecycle methods** are functions React calls automatically as a class component mounts, updates, and unmounts. Mount order: `constructor` → `render` → `componentDidMount`. ```jsx // Hook equivalent covers all three phases: useEffect(() => { // componentDidMount + componentDidUpdate logic return () => { /* componentWillUnmount cleanup */ }; }, [deps]); ``` **Key point:** use `componentDidMount` for API calls, `componentWillUnmount` for cleanup. In new code, `useEffect` replaces all three phases.Shown above the full answer for quick recall.Answer (EN)Image**React component lifecycle methods** are predefined functions in class components that React calls automatically at specific points: when a component appears in the DOM, updates, or gets removed. ## Theory ### TL;DR - Three phases: Mounting (component created), Updating (props/state changed), Unmounting (component removed) - Mount order: `constructor` → `render` → `componentDidMount`. Always in that sequence. - Side effects like API calls and timers go in `componentDidMount`, not in `render` or `constructor` - Hooks replicate all three phases via `useEffect` with a dependency array - New code: use hooks. Existing class components: stick with lifecycle methods ### Quick Example ```jsx class Timer extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; console.log('1. Constructor'); } componentDidMount() { // DOM is ready here, safe to start side effects this.timer = setInterval(() => { this.setState({ count: this.state.count + 1 }); }, 1000); console.log('3. DidMount - timer started'); } componentWillUnmount() { clearInterval(this.timer); // prevents memory leak console.log('Unmounting - timer cleared'); } render() { console.log('2. Render'); return <div>Count: {this.state.count}</div>; } } // Output on mount: 1. Constructor → 2. Render → 3. DidMount ``` `constructor` runs first, `render` builds the virtual DOM, then `componentDidMount` fires after the real DOM is ready. That order is fixed. ### The Three Phases **Mounting** is when a component first appears in the DOM: | Method | When called | |--------|-------------| | `constructor` | Component instance created, state initialized | | `static getDerivedStateFromProps` | Before every render (rarely needed) | | `render` | Returns JSX. Must be a pure function. | | `componentDidMount` | After the DOM is painted and ready | **Updating** happens when props or state change: | Method | When called | |--------|-------------| | `static getDerivedStateFromProps` | Before re-render | | `shouldComponentUpdate` | Can return `false` to skip re-render | | `render` | Produces updated JSX | | `getSnapshotBeforeUpdate` | Reads DOM before update (scroll position, etc.) | | `componentDidUpdate` | After DOM updates, safe for side effects | **Unmounting** has one method: `componentWillUnmount`. It fires before React removes the component. Clean up timers, event listeners, and pending requests here. ### Key Difference from Hooks Class lifecycle methods follow a strict call order enforced by React's Fiber reconciler. `render` never runs during unmount. `componentDidMount` always fires after the DOM commit. That predictability makes class components easier to reason about in complex scenarios. `useEffect` folds all three phases into one function. A wrong dependency array causes either stale data or an infinite re-render loop. Class methods make that bug impossible by design. ### When to Use Each Method - Initial data fetch: `componentDidMount` - Re-fetch when a prop changes: `componentDidUpdate` with a `prevProps` comparison - Skip unnecessary re-renders: `shouldComponentUpdate` (or `React.memo`) - Cleanup timers and subscriptions: `componentWillUnmount` - Sync state from props: `getDerivedStateFromProps` (rare, prefer `useEffect`) - Capture scroll before a DOM update: `getSnapshotBeforeUpdate` ### Class Lifecycle vs Hooks | Aspect | Class Lifecycle | Hooks (`useEffect`) | |--------|-----------------|---------------------| | Phases | Mounting / Updating / Unmounting (9+ methods) | Single effect with deps array | | Order guarantee | Strict and predictable | Runs after render; deps control re-runs | | Cleanup | `componentWillUnmount` | Return function from `useEffect` | | Performance | `shouldComponentUpdate` for fine control | `React.memo` + `useMemo` in React 18+ | | Learning curve | More API surface to memorize | Simpler, but dependency arrays are tricky | | When to use | Legacy codebases | All new code | ### How React Runs This Internally React's Fiber reconciler builds a work-in-progress tree during reconciliation. It can pause and resume that work to prioritize user input over background updates. After the tree commits to the real DOM, React fires `componentDidMount` or `componentDidUpdate`. Unmounting triggers `componentWillUnmount` before the tree is deleted. In React 18's concurrent mode, effects batch and defer, which is part of why hooks behave slightly differently from class lifecycle methods. ### Common Mistakes **API call in `render`:** ```jsx // Wrong - runs on every render, blocks the UI render() { const data = fetch('/api/todos'); return <div>{data}</div>; } // Correct componentDidMount() { fetch('/api/todos') .then(res => res.json()) .then(data => this.setState({ data })); } ``` `render` must be pure. No async calls, no side effects. **Missing cleanup in `componentWillUnmount`:** ```jsx componentDidMount() { this.timer = setInterval(tick, 1000); // No cleanup = memory leak on every route change } componentWillUnmount() { clearInterval(this.timer); // This line is the whole point } ``` Missing cleanup in `componentWillUnmount` is the most common React memory leak I've seen in production apps. In a React Router setup, components mount and unmount on every navigation, so intervals and subscriptions stack up fast. **Calling `setState` in `componentWillUnmount`:** ```jsx componentWillUnmount() { this.setState({ done: true }); // Warning: can't update unmounted component } ``` The component is already gone. React ignores the update and logs a warning. Use a boolean flag checked in `componentDidUpdate` instead. **No `prevProps` guard in `componentDidUpdate`:** ```jsx // Wrong - infinite loop componentDidUpdate() { this.fetchData(); // triggers after every update, including updates it causes } // Correct componentDidUpdate(prevProps) { if (this.props.userId !== prevProps.userId) { this.fetchData(); } } ``` Always guard `componentDidUpdate`. Without a condition, you get an infinite render loop. ### Real-World Usage - React Router: `componentDidMount` fetches route-specific data (user profile, post detail) - Redux-Observable: subscribe to epics in `componentDidMount`, unsubscribe in `componentWillUnmount` - Chat apps: `getSnapshotBeforeUpdate` captures scroll position, `componentDidUpdate` restores it after new messages load above the viewport - Next.js: `componentDidMount` runs only on the client, useful for browser-only APIs like `localStorage` - React 18 migration: replacing class lifecycle with `useEffect` unlocks concurrent rendering features ### Follow-up Questions **Q:** What is the mounting order when a parent renders a child component? **A:** Parent constructor, parent render, child constructor, child render, child `componentDidMount`, then parent `componentDidMount`. Children always mount before parents finish mounting. **Q:** Why not make API calls in `constructor`? **A:** `constructor` runs before the component is in the DOM. There are no guarantees about DOM state, and in SSR (Next.js, Remix) it causes hydration mismatches. `componentDidMount` is the correct place. **Q:** What is `getSnapshotBeforeUpdate` actually used for? **A:** It runs right before React applies DOM changes. You can read current DOM values like scroll position and return them to `componentDidUpdate` as a third argument. Most common use: preserving chat scroll when new messages load above the current view. **Q:** Can `shouldComponentUpdate` introduce bugs? **A:** Yes. Returning `false` when the component should update breaks rendering with no error thrown. The same risk exists with `React.memo` and a custom comparator. Always profile with React DevTools Profiler before adding this optimization. **Q:** In React 18 concurrent mode, can lifecycle methods run more than once? **A:** `render` can be invoked multiple times before a commit. But `componentDidMount` and `componentDidUpdate` still fire once per commit. This is a core reason React 18 favors hooks: `useEffect` has clearer semantics in concurrent scenarios. ## Examples ### Basic: Tracking Lifecycle Order ```jsx class LifecycleDemo extends React.Component { constructor(props) { super(props); this.state = { name: props.name }; console.log('1. constructor'); } static getDerivedStateFromProps(props, state) { console.log('2. getDerivedStateFromProps'); return null; } componentDidMount() { console.log('4. componentDidMount - DOM ready'); } componentDidUpdate(prevProps, prevState) { console.log('5. componentDidUpdate'); } componentWillUnmount() { console.log('6. componentWillUnmount - cleanup here'); } render() { console.log('3. render'); return <div>Hello, {this.state.name}</div>; } } ``` Run this in a browser to see the exact order React uses. `getDerivedStateFromProps` fires before every `render`, including updates. That surprises most developers the first time. ### Intermediate: Todo List with API Fetch ```jsx class TodoList extends React.Component { state = { todos: [], loading: true }; componentDidMount() { this.fetchTodos(); } componentDidUpdate(prevProps) { // Re-fetch only when the filter prop changes if (this.props.filter !== prevProps.filter) { this.fetchTodos(); } } fetchTodos() { this.setState({ loading: true }); fetch(`/api/todos?filter=${this.props.filter}`) .then(res => res.json()) .then(todos => this.setState({ todos, loading: false })); } render() { if (this.state.loading) return <div>Loading...</div>; return ( <ul> {this.state.todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ); } } ``` `componentDidMount` handles the initial load. `componentDidUpdate` handles prop-driven re-fetches without duplicating the fetch logic. The `prevProps` check prevents an infinite loop. ### Advanced: Debounced Search with Cleanup ```jsx class SearchResults extends React.Component { state = { results: [] }; componentDidUpdate(prevProps) { if (this.props.query !== prevProps.query) { this.scheduleFetch(); } } scheduleFetch = () => { // Arrow function keeps 'this' correct clearTimeout(this.timeout); this.timeout = setTimeout(() => { fetch(`/api/search?q=${this.props.query}`) .then(res => res.json()) .then(data => this.setState({ results: data.results })); }, 300); }; componentWillUnmount() { clearTimeout(this.timeout); // cancel pending fetch if user navigates away } render() { return ( <ul> {this.state.results.map(r => <li key={r.id}>{r.title}</li>)} </ul> ); } } ``` Two things keep this correct. The debounce avoids a request on every keystroke. The cleanup in `componentWillUnmount` cancels any pending timeout if the component unmounts before the 300ms window closes, preventing a `setState` call on an unmounted component.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.