Functional Component — is a way to create React components using regular JavaScript functions, which became the standard after the release of React 16.8 and the introduction of hooks. Functional components accept props and return React elements without requiring inheritance from React.Component. According to React, 2024, 97% of new components in the React ecosystem are written as functions.
Key Takeaways
Functional Component is a React component defined as a function that accepts a props object and returns a React element. Before React 16.8, such components were called stateless because they couldn't manage internal state.
With the introduction of hooks in React 16.8 (February 2019), functional components gained access to state (useState), side effects (useEffect), context (useContext) and refs (useRef). The term “stateless” is outdated — modern functional components fully manage state.
React officially recommends functional components as the primary way to create components. All documentation on react.dev, examples, tutorials and new APIs (Server Components, Actions) are built on the functional approach. Class components haven't been removed, but new React features are only available through hooks.
The simplest functional component is a function that returns JSX. It accepts props as an argument, requires no constructor, super call or this binding. Props are accessible directly through the function argument — no this.props needed.
// Functional component without state
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function (common variant)
const Greeting = ({ name }) => (
<h1>Hello, {name}!</h1>
);
To add state, use useState. The hook returns a tuple of two values: the current state and a setter function. Unlike class-based this.setState, useState replaces the value entirely rather than merging it with the previous one.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Hooks are the foundation of modern React. Each hook solves a specific task: useState — state management, useEffect — side effects, useContext — context access, useRef — DOM element references, useCallback and useMemo — memoization.
useEffect replaces componentDidMount, componentDidUpdate and componentWillUnmount all at once. The first argument is a callback with the effect, the second is a dependency array. If the array is empty, the effect runs once after mounting. If dependencies are specified, it runs whenever any of them changes.
import React, { useState, useEffect } from 'react';
function UserData({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
if (!cancelled) setUser(data);
});
// Cleanup - analog of componentWillUnmount
return () => { cancelled = true; };
}, [userId]); // Dependency
return <div>{user?.name}</div>;
}
The main advantage of functional components is logic reuse through custom hooks. While in a class component logic was duplicated between componentDidMount and componentDidUpdate, in a functional component it is extracted into a separate function with the use prefix. Custom hooks are the primary alternative to HOC and render props.
React recommends composition rather than inheritance for code reuse between components. Functional components are ideal for composition: one component can contain several others, passing them props and children.
The children pattern — when a component receives child elements through the children prop — is implemented naturally: function Layout({ children }). The Vue slots analog is implemented through named component props: function Page({ header, sidebar, content }).
Inheritance is barely used in React. Even class components, which technically support extends, rarely form hierarchies deeper than one level. Functional components with composition fully cover code reuse needs without inheritance.
Functional components come with built-in optimization mechanisms. React.memo — the PureComponent equivalent for functions: prevents re-rendering if props haven't changed (shallow comparison). useMemo caches computation results between renders.
import React, { useMemo } from 'react';
// React.memo - memoize component
const ExpensiveList = React.memo(({ items }) => {
return <ul>
{items.map(item =>
<li key={item.id}>{item.name}</li>
)}
</ul>;
});
// useMemo - caching result
function Dashboard({ transactions }) {
const summary = useMemo(() => {
return transactions.reduce((acc, t) => acc + t.amount, 0);
}, [transactions]);
return <p>Total: {summary}</p>;
}
useCallback memoizes callback functions so they aren't recreated on every render. This is critical when passing functions to child components wrapped in React.memo: without useCallback, the child component will re-render every time because the function reference changes.
The difference between the approaches goes beyond syntax. Functional components are closures: state and effects are tied to a specific render. Class components use this: state is always retrieved from this.state, which may be outdated by the time an async callback executes.
| Aspect | Functional | Class |
|---|---|---|
| State | useState (isolated) | this.state + this.setState |
| Effects | useEffect with dependencies | componentDidMount + componentDidUpdate |
| Context | useContext | Context.Consumer or static contextType |
| Optimization | React.memo + useMemo | PureComponent + shouldComponentUpdate |
| Logic Reuse | Custom hooks | HOC / Render props |
| this | None | bind in constructor |
Functional components provide a linear execution flow. In a class component, related logic is often scattered across multiple lifecycle methods. For example, a WebSocket subscription is set up in componentDidMount and torn down in componentWillUnmount. In a functional component, both operations are in a single useEffect with a return cleanup function.
TypeScript works more naturally with functional components than with classes. Props typing — an interface for the function argument. The return type is JSX.Element or ReactNode. useState automatically infers the type from the initial value.
import React, { useState } from 'react';
interface ButtonProps {
label: string;
variant?: 'primary' | 'secondary';
onClick: () => void;
}
const Button: React.FC<ButtonProps> =
({ label, variant = 'primary', onClick }) => (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{label}
</button>
);
Modern TypeScript allows typing useReducer with discriminated union types, Generics in custom hooks, and strict children validation through React.PropsWithChildren. Functional components with TypeScript provide complete type safety without additional abstractions.
Frequently Asked Questions
Functional components are simpler: no constructor, bind, this or scattered lifecycle methods. Hooks allow grouping related logic. React invests resources in developing hooks, not classes. Server Components and new features are only available in functions.
Yes, through useEffect with a timer inside and a cleanup function for clearInterval. Pay attention to closure: if you're ticking a counter, use the functional form of setState: setCount(c => c + 1) to avoid depending on the old value in the closure.
Use useRef to store the previous value. Update the ref in useEffect after each render. A custom hook usePrevious helps for clean comparison: const prev = usePrevious(value);
React.memo is an HOC for memoizing an entire component: prevents re-rendering if props haven't changed. useMemo is a hook for memoizing a specific value inside a component: caches a function result between renders by a dependency array.
The simplest way is useState with initialization from props + useEffect with a dependency on props for synchronization. For rare cases, React recommends using a key to force component recreation when input data changes.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also