Suggest an editImprove this articleRefine the answer for “Difference between functional and Class components in React”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Functional components** are JavaScript functions that return JSX and use hooks for state and lifecycle. Class components are ES6 classes with `this.state` and lifecycle methods. ```jsx // Functional const Hello = ({ name }) => <h1>Hello, {name}</h1>; // Class class Hello extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } } ``` **Key point:** Use functional components by default. Class components are only needed for error boundaries.Shown above the full answer for quick recall.Answer (EN)Image**Functional components** are JavaScript functions that receive props and return JSX. Class components are ES6 classes that extend `React.Component`, manage state via `this.state`, and hook into the React lifecycle through dedicated methods. ## Theory ### TL;DR - Functional component = a function call. Class component = a long-lived object with methods React calls at specific moments. - Since React 16.8, hooks (`useState`, `useEffect`) let functional components do anything classes can. - One exception: error boundaries. Only class components support `componentDidCatch`. - New code default: functional component. Always. ### Quick example ```jsx // Functional: hook handles state, no ceremony function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(count + 1)}>{count}</button>; } // Class: same result, more setup required class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <button onClick={() => this.setState({ count: this.state.count + 1 })}> {this.state.count} </button> ); } } ``` The functional version is typically 30-50% shorter. Both produce identical output. ### Key difference When React renders a functional component, it calls the function with props and captures the returned JSX. Each render is a fresh call with its own scope. A class component works differently: React creates one instance and stores it. Then it calls `render()` on that instance every time an update happens. The instance persists. That is why `this.state` stays attached to the component without hooks, and also why you have to think about method binding every time you write a class. ### When to use - **Functional:** any new feature, any new project, any component that needs state or side effects - **Class:** error boundaries (the only way to catch render errors with `componentDidCatch`), legacy class code you are maintaining ### Comparison table | Aspect | Functional | Class | |--------|-----------|-------| | Syntax | `function MyComponent(props) {}` | `class MyComponent extends React.Component {}` | | State | `useState()` hook | `this.state` + `setState()` | | Lifecycle | `useEffect()` | `componentDidMount()`, `componentDidUpdate()`, etc. | | Context | `useContext()` | `this.context` | | `this` binding | Not needed | Required, must bind or use arrow class fields | | Code size | Typically 30-50% smaller | More verbose | | Error boundaries | Not supported | Supported via `componentDidCatch()` | | When to use | Default for all new code | Error boundaries, legacy code | ### Common mistakes **Calling hooks conditionally** ```jsx // WRONG: hook order breaks between renders function Component({ showEmail }) { if (showEmail) { const [email, setEmail] = useState(""); // shifts call order } } // RIGHT: always call hooks at the top level function Component({ showEmail }) { const [email, setEmail] = useState(""); // render email field only if showEmail is true } ``` React tracks hooks by their position in the call order. A conditional call shifts that order between renders and attaches state to the wrong hook. **Missing dependency array in useEffect** ```jsx // WRONG: runs after every render, triggers infinite loop useEffect(() => { fetch("/api/data").then(r => r.json()).then(setData); }); // RIGHT: empty array = runs once on mount useEffect(() => { fetch("/api/data").then(r => r.json()).then(setData); }, []); ``` **Forgetting to bind methods in class components** ```jsx // WRONG: this is undefined inside the callback class Button extends React.Component { handleClick() { console.log(this.state); // TypeError } render() { return <button onClick={this.handleClick}>Click</button>; } } // RIGHT: arrow class field keeps this class Button extends React.Component { handleClick = () => { console.log(this.state); // component instance }; render() { return <button onClick={this.handleClick}>Click</button>; } } ``` ### Real-world usage - React docs and official examples: all functional components since React 16.8 - Next.js: functional by default; class components appear only as error boundaries - Redux: `useSelector` and `useDispatch` hooks replaced the `connect()` HOC pattern - React Query: `useQuery` and all other query hooks require functional components - Error boundaries: class-only. No functional equivalent exists in current React versions. In most codebases I have worked with, there is a single `ErrorBoundary` class component at the root, and everything else is functional. ### Follow-up questions **Q:** Why can't error boundaries be functional components? **A:** Error boundaries need `componentDidCatch` and `getDerivedStateFromError`, which are class-specific lifecycle methods. React has not shipped a hook equivalent. The standard approach is one class component wrapping the full app tree. **Q:** Do functional components with hooks perform better than class components? **A:** Not inherently. Both compile to similar output. Functional components are easier to optimize because you can split logic into separate hooks and memoize each piece with `useMemo` and `useCallback`. With classes, you need `shouldComponentUpdate` or `PureComponent` for the same control. **Q:** What is a stale closure and why does it only come up in functional components? **A:** A stale closure happens when a callback captures an old value of a state variable. Class components avoid this because `this.state` always points to the current instance. In functional components, fix it by using the updater form: `setCount(prev => prev + 1)` instead of `setCount(count + 1)`. **Q:** How do you replace class lifecycle methods with hooks? **A:** One `useEffect` per concern. `useEffect(() => {...}, [])` replaces `componentDidMount`. `useEffect(() => {...}, [dep])` replaces `componentDidUpdate` for a specific value. Returning a cleanup function from any effect replaces `componentWillUnmount`. ## Examples ### Login form with validation ```jsx function LoginForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [errors, setErrors] = useState({}); const handleSubmit = (e) => { e.preventDefault(); const newErrors = {}; if (!email.includes("@")) newErrors.email = "Invalid email"; if (password.length < 8) newErrors.password = "Min 8 characters"; if (Object.keys(newErrors).length > 0) { setErrors(newErrors); return; } console.log("Submitting..."); }; return ( <form onSubmit={handleSubmit}> <input value={email} onChange={(e) => setEmail(e.target.value)} /> {errors.email && <span>{errors.email}</span>} <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> {errors.password && <span>{errors.password}</span>} <button type="submit">Login</button> </form> ); } ``` Three state variables, validation logic, no `this`, no binding. Each piece of state lives next to the logic that uses it. ### Error boundary (the one case for class components) ```jsx class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError() { return { hasError: true }; } componentDidCatch(error, info) { console.error("Caught render error:", error, info); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } ``` This is not a legacy pattern. It is the only option React currently offers for catching render errors. Wrap it around your app once.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.