Building React Components Using Functions
Functional components make React code simpler and more predictable. This post shows how to build maintainable, testable UI (user interface) pieces using functions, with practical patterns and focused examples.
What is a functional component?
A functional component is a plain JavaScript function that returns JSX (JavaScript XML), the HTML-like syntax React uses to describe UI. React calls the function and renders whatever it returns.
Example:
function App() {
return <h1>Hello React</h1>;
}
export default App;
Think of a functional component like a recipe: you give it inputs and it returns a finished dish (the UI). Recipes are easy to read and combine.
Summary: Functional components are simple functions that produce UI with JSX.
Props and children: passing data into functions
Props (properties) are the inputs to a component. You receive them as a single object and use destructuring to keep code readable. children is a special prop for nested content.
Example:
function Badge({ label, color = "gray", children }) {
return (
<div style={{ background: color, padding: 8, borderRadius: 6 }}>
<strong>{label}</strong>
<div>{children}</div>
</div>
);
}
Use props to keep components pure and reusable. Prefer small focused props over one big object.
Summary: Use props and children to configure and nest components.
State and Hooks: managing local data
React Hooks let functional components hold state and side effects. Hooks are plain functions that start with "use". Two essential Hooks are useState and useEffect.
Example counter:
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<span>{count}</span>
</div>
);
}
Rules of Hooks: call them only at the top level of a React function and only from React functions. Hooks keep logic colocated and easier to test.
Summary: Hooks provide state and lifecycle behavior inside functions.
Composition and reuse: assemble small pieces
Favor composition over copying logic. Build small components and combine them. Custom Hooks let you extract repeated logic (like form handling or toggles).
Custom hook example:
import { useState } from "react";
function useToggle(initial = false) {
const [on, setOn] = useState(initial);
const toggle = () => setOn((v) => !v);
return { on, toggle };
}
// Usage
function ToggleButton() {
const { on, toggle } = useToggle();
return <button onClick={toggle}>{on ? "ON" : "OFF"}</button>;
}
Composition is like Lego blocks: each block does one thing and you combine them to build complex shapes.
Summary: Extract logic into components/hooks and compose them.
Performance patterns: memo, useCallback, useMemo
Functional components are fast, but sometimes you need micro-optimizations for expensive renders. Use React.memo to skip rendering when props don't change. Use useCallback and useMemo to avoid recreating functions/value objects unnecessarily.
Example:
import React, { useCallback } from "react";
const Item = React.memo(function Item({ onClick, label }) {
return <button onClick={onClick}>{label}</button>;
});
function List({ items, onItem }) {
const handleClick = useCallback((id) => onItem(id), [onItem]);
return items.map((it) => <Item key={it.id} onClick={() => handleClick(it.id)} label={it.name} />);
}
Only optimize when you measure a real problem. Premature memoization can add complexity without benefit.
Summary: Apply memoization selectively and measure first.
Testing functional components
Test behavior, not implementation. Use a DOM testing tool like Testing Library for user-focused tests. Keep components small and inject dependencies via props to simplify testing.
Example test (pseudo):
// Using @testing-library/react
render(<Counter />);
userEvent.click(screen.getByText("+1"));
expect(screen.getByText("1")).toBeInTheDocument();
Small, focused components lead to simpler tests.
Summary: Test user interactions and state transitions with small components.
Migrating from class components (quick notes)
You rarely need class components today. Convert lifecycle methods to useEffect and state to useState or custom hooks. The mental model becomes simpler: input -> function -> output.
Keep one UI change per refactor to reduce risk.
Summary: Move logic into hooks and keep components focused.
Conclusion: start small and compose
Build components as small functions, use Hooks for state and effects, and compose pieces like Lego. Try migrating one screen or widget in your app to functional components this week. If you want, share a small component and I’ll suggest a refactor to a functional pattern.
Next step: pick a simple component from your codebase and convert it to a function with a custom hook for any stateful logic.

