Function Components
Function components as UI functions of props and state: hooks, purity under concurrent render, and how they differ from classes.
- react
- function-components
A function component is a JavaScript function that accepts props and returns React elements describing the UI. With Hooks, functions own state, effects, and refs — the modern default for almost all new React UI.
function Greeting({ name }) {
return <p>Hello, {name}</p>;
}
React calls your function during render. You return a description; React decides what to change in the DOM during commit. Treat the body as pure with respect to props and state: same inputs should produce the same element tree (unless you deliberately accept non-determinism).
Docs: Your First Component, Passing Props.
Anatomy with hooks
import { useState, useEffect } from 'react';
function UserCard({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/users/${userId}`)
.then((r) => r.json())
.then((data) => {
if (!cancelled) setUser(data);
});
return () => {
cancelled = true;
};
}, [userId]);
if (!user) return <p>Loading…</p>;
return (
<article>
<h2>{user.name}</h2>
<p>{user.bio}</p>
</article>
);
}
Conventions that matter:
- Capitalized name —
UserCard, notuserCard. Lowercase is treated as an intrinsic host tag. - Props object — one argument; destructure for clarity.
- Hooks at the top level — see Rules of Hooks.
- No direct DOM mutation during render — leave that to effects or refs after commit.
Function versus class
| Concern | Function + Hooks | Class |
|---|---|---|
| State | useState / useReducer |
this.state |
| Side effects | useEffect / useLayoutEffect |
lifecycle methods |
| Instance methods | rare; callbacks + refs | this.handleClick |
| Error boundaries | not available in functions | componentDidCatch |
| Mental model | composition-friendly | more boilerplate |
New code should be functions unless you need an error boundary class or are maintaining a legacy tree. See Error Boundaries.
Purity and concurrent rendering
React may call your function multiple times before commit (Strict Mode double-invoke in development, concurrent interruptions). That is why side effects during render are bugs:
// Bad: side effect during render
function Bad({ id }) {
analytics.track('view', id);
return <div />;
}
// Good: effect after paint
function Good({ id }) {
useEffect(() => {
analytics.track('view', id);
}, [id]);
return <div />;
}
Local pure calculations during render are fine. Avoid derived state anti-patterns that copy props into state.
Props are read-only
Never mutate props. To change data, lift state or call a callback the parent provided:
function Toggle({ on, onChange }) {
return (
<button type="button" aria-pressed={on} onClick={() => onChange(!on)}>
{on ? 'On' : 'Off'}
</button>
);
}
Interview out-loud
“Function components are pure-ish functions from props and state to elements. Hooks attach state and effects to the fiber. React may re-run the function without committing, so keep render free of side effects. Prefer functions over classes except for error boundaries.”
Related on this site
Further reading
Edge cases worth rehearsing
Interviewers and production incidents cluster around the same edges: first render versus update, empty and loading states, Strict Mode double setup, concurrent interruptions, and what happens when identity (key, route params, user id) changes mid-edit. Walk one concrete user journey end-to-end — open, edit, navigate away, come back — and say which state survives.
Prefer fixing data flow and ownership before reaching for memoization or micro-optimizations. Prefer event handlers over effects when a user action is the trigger. Prefer deriving values during render over mirroring props into state. Prefer stable list keys from business ids. Measure with the profiler when performance is the claim.
When you cite an API, mention one failure mode: abort on unmount, serializable props across server/client boundaries, focus restoration for dialogs, or cache invalidation after a mutation. Specific beats generic every time.
Related guides
- useCopyToClipboard HookCopy text with navigator.clipboard, fallback execCommand, transient copied state, and permissions that fail in insecure contexts.
- useDebouncedValue HookDebounce a changing value in React without firing effects on every keystroke. Cleanup, leading vs trailing, and the stale timer that updates unmounted state.
- useEventListener HookAttach a window, document, or element listener with a stable handler ref so you do not rebind on every render. Capture option and target changes.
- useFetch With AbortControllerFetch in an effect with a signal, ignore aborted errors, and prevent the slow response from winning a race after the url changes.
- useIdle HookDetect idle users: reset a timer on input events, fire idle after timeout, and decide whether mount starts the timer immediately.