skip to content

Component Anatomy and Typing

What a React component actually is: a function of props that returns elements. Interviewers use the function-vs-class question and prop typing to check whether you understand render purity and the modern authoring style rather than reciting legacy lifecycle names.

part ofReactoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In React 19, what exactly is a function component, how does it differ from a class component, and is there anything a class can still do that a function cannot?

level: juniorimportance: must knowfreq 80%

answer

  1. props in, elements out
  2. hooks need a function, not a class
  3. this.props is live; closures capture
  4. one thing classes still uniquely do

basics

~20 s

A function component is a plain function that takes a props object and returns React elements, using hooks for state and effects. A class extends React.Component and implements render() plus lifecycle methods. Only error boundaries still require a class.

solid answer

~50 s

A React component is a function of its inputs that returns a description of UI. In the function form you write `function Greeting({ name }) { return <h1>Hello {name}</h1>; }` — one argument, the props object, and a return value made of React elements; state, context and side effects come from hooks. The class form extends `React.Component`, stores props on `this.props`, calls `this.setState`, and must implement `render()`. Two differences bite in practice: a function closes over the props of the render it ran in, while `this.props` in a class always reads the latest values, and hooks cannot be called inside a class. Classes are not deprecated, but the one thing they still uniquely do is act as an error boundary, via `static getDerivedStateFromError` and `componentDidCatch`. Everything else is written as functions in new React 19 code.

code

javascript · 19 lines
javascript
function Greeting({ name, children }) {
  return (
    <section>
      <h1>Hello, {name}</h1>
      {children}
    </section>
  );
}

class GreetingClass extends React.Component {
  render() {
    return (
      <section>
        <h1>Hello, {this.props.name}</h1>
        {this.props.children}
      </section>
    );
  }
}

go deeper

for a junior

Be able to write a function component from memory, say that it takes props and returns elements, and state plainly that state and effects come from hooks rather than lifecycle methods.

for a middle

Explain the mechanics: a class has a persisting instance whose this.props always reads the newest values, while each function render is a fresh call whose closures capture that render's props. Name error boundaries as the class-only case.

for a senior

Show judgment about legacy code — when converting a class earns its keep, when wrapping it is enough, and how you keep one error-boundary class without letting class patterns spread back into new code.

for a principal

Own the migration policy: whether the codebase forbids new classes outright, how conversions are sequenced against feature work, and what the Rules of React and the compiler cost you if a mixed codebase drifts.

## What a component actually is Strip away the syntax and a React component is a function from inputs to a description of UI. The inputs are its props, plus whatever it reads from state and context; the output is React elements — plain JavaScript objects that describe what should appear on screen. Elements are not DOM nodes. React calls your component, receives that description, compares it with the previous one, and takes sole responsibility for touching the DOM. Everything else about the word "component" is authoring style layered on top of that one idea. One mechanical rule follows from it: a component used in JSX must be referenced by a capitalized name (or a dotted path like `Menu.Item`). A lowercase name in JSX is treated as a built-in DOM tag, which is why `<greeting />` renders an unknown HTML element instead of calling your function. ## The function form ```javascript function Greeting({ name, children }) { return ( <section> <h1>Hello, {name}</h1> {children} </section> ); } ``` A function component takes exactly one argument — the props object. `children` is an ordinary prop. In React 19 `ref` is an ordinary prop too, so a function component can accept a ref without `forwardRef`. State comes from `useState` or `useReducer`, synchronization with external systems from `useEffect`, shared data from `useContext`. The function body runs on every render, top to bottom, and every local variable inside it is recreated each time. ## The class form ```javascript class Greeting extends React.Component { state = { open: false }; render() { return <h1>Hello, {this.props.name}</h1>; } } ``` A class component is instantiated by React once per mounted position in the tree. That instance persists across renders and holds `this.props`, `this.state` and `this.setState`. `render()` is the only required method; the lifecycle methods are optional hooks into mount, update and unmount. `defaultProps` and `contextType` are class features that still work. ## The differences an interviewer is probing **Instance versus closure.** A class has an object identity that survives renders, so `this.props` is a live reference that always yields the newest values — even inside an asynchronous callback scheduled several renders ago. A function component has no instance; each render is a separate call whose variables are captured by any closure created inside it. That is why a `setTimeout` in a function component sees the props from the render that created it, and the same code in a class sees the latest. Neither is a bug; they are different capture semantics, and knowing which you are in is the point. **Hooks are function-only.** Hooks are dispatched from the currently rendering function component, so they cannot be called in a class body or in `render()`. A class needing hook-only behaviour has to be wrapped by a function that reads the hook and passes the result down as a prop. **Ceremony.** Classes require binding handlers or class-field arrow functions to keep `this`, and they scatter one concern across several lifecycle methods. Functions keep a concern in one place. ## What only classes can still do Error boundaries. There is no hook that catches a rendering error from the subtree below; the API is `static getDerivedStateFromError` (to render a fallback) and `componentDidCatch` (to log). Real codebases keep one small class for this, or use a library that wraps one. That is the honest answer to "is anything class-only left" — and it is the only such item worth naming. ## Why functions became the default Because logic reuse works. Extracting a piece of stateful behaviour from a function component into a custom hook is a copy-paste of the hook calls; extracting the same thing from a class historically meant a higher-order component or a render prop, both of which distort the tree. The React Compiler also targets function components written to the Rules of React, so the modern optimization story assumes this style. Classes still render fine, they are still supported, and reading them is a required skill — but you should not present one as the way you would write new code.

  • If a class always reads the latest values through this.props, why is that not simply better than a function's captured props?
    Because "latest" is often wrong. A callback scheduled during one render usually wants the values from that render — the request it was describing, the item the user clicked. A class silently gives it newer data, which produces inconsistent mixes of old and new state. Function components make the capture explicit, so you choose deliberately when you want the freshest value.
  • Can a class component use a hook if you really need one?
    Not directly — hooks are dispatched from the rendering function component, so calling one inside a class throws. The standard workaround is a thin function wrapper: it calls the hook and passes the result down as a prop to the class. In new code you would convert the class instead.
  • Are class components deprecated in React 19?
    No. They still render, `render()` and the lifecycle methods still work, and `defaultProps` is still honoured for classes. What has happened is that every new API — hooks, Actions, the compiler — targets function components, so classes are effectively legacy: something you read and migrate rather than write.

saying these in an interview costs you the question

  • Claiming class components are removed or broken in React 19
  • Saying function components cannot hold state
  • Calling function components stateless or presentational
  • Believing hooks work inside class components with a wrapper decorator
  • Naming lifecycle-to-hook mapping as the only difference that matters

context

open as a page

React requires components to be pure during render. What does purity mean for a React component, and which kinds of code violate it?

level: middleimportance: must knowfreq 62%

basics

~20 s

A pure React component returns the same elements for the same props, state and context, and changes nothing outside itself while rendering: no mutating props or existing objects, no writing to module or global variables, no I/O. Side effects belong in event handlers or effects.

open as a page

React 19 removed support for defaultProps on function components. How do you give a function component's props default values now, and what is left of defaultProps?

level: middleimportance: should knowfreq 45%

basics

~10 s

Use JavaScript default parameter values in the destructuring of the props argument, such as function Button({ size = 'medium' }). React 19 ignores defaultProps on function components; it is still honoured on class components.

open as a page

In a React + TypeScript codebase, how do you type a function component's props, and why do most teams avoid React.FC?

level: middleimportance: should knowfreq 55%

basics

~20 s

Type the props argument directly: function Button({ label }: ButtonProps). React.FC adds nothing you need, forces the return type, and since @types/react 18 no longer implies children, so teams declare children explicitly with React.ReactNode instead.

open as a page

A reusable Button in a shared component library spreads every unrecognized prop onto the underlying <button> with {...rest}. What risks does that create, and how would you control it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Blind spreading leaks unknown props into the DOM, lets callers silently override the component's own attributes depending on spread order, and hides the real API from readers and types. Control it by destructuring what you own, spreading the rest first, and merging className and handlers deliberately.

open as a page