Reasons for Advocating Functional Programming in React
A record of exploring the pros and cons of functional programming and object-oriented programming, and the reasons for choosing functional programming in React
A record of exploring the pros and cons of functional programming and object-oriented programming, and the reasons for choosing functional programming in React
The password you enter is used to open, edit, and delete secret comments.
React has been leaning towards Functional Programming (FP) in recent years.
It actively leverages the advantages of functional approaches from simple UI components to complex application structures.
In this article, we will systematically organize the reasons and grounds for advocating functional programming in React.
The core of functional programming is the 'pure function'. A pure function is a function that always produces the same output for the same input and does not alter external state.
Class Component Testing:
class UserProfile extends React.Component {
state = { name: '', loading: false };
async componentDidMount() {
this.setState({ loading: true });
const user = await fetchUser(this.props.userId);
this.setState({ name: user.name, loading: false });
}
render() {
return <div>{this.state.loading ? 'Loading...' : this.state.name}</div>;
}
}
Functional Component:
function UserProfile({ userId }) {
const { user, loading } = useUser(userId);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
// Only need to check props during testing (Hook is tested separately)Functional components allow for separation of logic and rendering, enabling independent testing.
Example of Pure Function:
function formatPrice(price, currency = 'USD') {
return `${currency} ${price.toFixed(2)}`;
}
// Predictable results based on input
expect(formatPrice(10.5)).toBe('USD 10.50');
expect(formatPrice(20, 'KRW')).toBe('KRW 20.00');In functional programming, data is handled immutably by default.
When immutability is maintained, React's optimization tools function very effectively:
Efficient Operation of React.memo:
// When maintaining immutability
const TodoItem = React.memo(({ todo, onToggle }) => (
<div onClick={() => onToggle(todo.id)}>
{todo.text} {todo.completed ? '✓' : '○'}
</div>
));
// No re-rendering if props have the same referenceAccurate Dependency Detection with useMemo and useCallback:
function TodoList({ todos, filter }) {
// Re-execute filtering only when the todos array is newly created
const filteredTodos = useMemo(() =>
todos.filter(todo => todo.category === filter),
[todos, filter]
);
// No function re-creation if dependencies do not change
const handleToggle = useCallback((id) =>
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
Wrong Method vs Correct Method:
// ❌ Direct modification - Optimization tools cannot detect changes
function badUpdate() {
const newTodos = todos;
newTodos[0].completed = true;
setTodos(newTodos); // No re-rendering because the reference is the same
}
// ✅ Creating new objects - Optimization tools work accurately
function goodUpdate() {
setTodos(prev => prev.map((todo, index) =>
index === 0 ? { ...todo, completed: true } : todo
));
}By maintaining immutability, state change detection is possible using only reference comparison («===»).
Deep Comparison vs Reference Comparison:
// ❌ Deep Comparison - Checks all properties recursively (slow)
function deepEqual(obj1, obj2) {
// Must compare all key-values of the object
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
// ✅ Reference Comparison - Only checks memory addresses (fast)
function shallowEqual(obj1, obj2) {
return obj1 === obj2; // Ends with a single comparison
}React uses Object.is() (almost identical to «===») when confirming whether a state has changed:
function Counter() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: 'John', age: 25 });
// ✅ Primitives automatically ensure immutability
const increment = () => setCount(prev => prev + 1);
// ✅ Objects must be newly created to detect changes
const updateAge = () => setUser(prev => ({ ...prev, age: prev.age + 1 }));
function DataTable({ items }) {
// No re-sorting if items have the same reference
const sortedItems = useMemo(() => {
console.log('Sorting executed!'); // Check when it executes
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
return (
<table>
{sortedItems.map(item => (
<tr key={item.id}>
<td>{item.name}</td>
By maintaining immutability like this, change detection in complex data structures occurs in O(1) time, providing significant performance benefits.
Memoization is an optimization technique that returns cached values instead of recalculating them for the same input. Functional programming assumes pure functions, making memoization highly effective.
useMemo and useCallback hooks are based on this ideafunction ProductList({ products, searchTerm }) {
// Does not refilter if searchTerm or products have not changed
const filteredProducts = useMemo(() => {
console.log('Executing filter!');
return products.filter(p =>
p.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return <div>{filteredProducts.map(/* ... */)}</div>;
}Difficult to Memoize Non-Pure Functions:
// ❌ Not pure - depends on external state
let discount = 0.1;
function calculatePrice(price) {
return price * (1 - discount); // References external variable
}
// ✅ Pure - all inputs are passed as parameters
function calculatePrice(price, discount = 0) {
return price * (1 - discount);
}This way, the "immutability + purity" of functional programming aligns with the prerequisites of memoization, strengthening both concepts.
By using functional components and Hooks (useState, useReducer), state management code becomes succinct.
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { id: Date.now(), text: action.text, completed: false }];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter(todo => todo.id !== action.id);
default:
return
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// Can be reused in multiple components
function Settings
By doing so, UI rendering logic and data logic can be clearly separated, improving readability.
The reason React adopts functional programming is due to clarity of code, ease of testing, optimization, and efficiency in change detection.
The functional paradigm is an effective strategy for modularizing components and controlling complexity.
This is my subjective judgment
Frontend (React) seems suitable for efficiently handling UI rendering, state change detection, optimization, and testing with the functional paradigm.
On the other hand, Backend (Node.js, Java, etc.) is considered practical with object-oriented programming (OOP) for performance issues, resource management, and process flow control.
| Category | Frontend (React, etc.) | Backend (Node.js, Java, etc.) |
|---|---|---|
| Key Considerations | UI Rendering Optimization, State Change Tracking | Performance, Resource Management, Transaction Flow |
| Suitable Paradigm | Functional Programming | Object-Oriented Programming |
| Advantages | Easy Testing, State Change Tracking with Immutability, Component Reusability | Clearly Defined Responsibilities with Class-Based Structure, State Maintenance, Connection-Centric Design, Ease of Structuring Complex Logic |
| Disadvantages | Inconvenient for Complex Flow Operations, Readability Decreases with Indiscriminate Hook Usage | Difficult to Separate State during Testing, Complex Re-Rendering Optimization Structure |
A record of the intense 7-week journey: from improving onboarding and infrastructure monitoring to implementing real-time sockets and 3D character optimization.
A record of the emergence, advantages, and disadvantages of UI architecture patterns (MVC, MVP, MVVM, Flux), and their comparative analysis.

A record of building the foundation: from ideation and prototyping to navigating senior feedback and implementing the MVP
An in-depth look at why we normalize vectors, connecting game movement logic with Blender's "Apply Scale."
A reflection on a 6-week team sprint: covering architectural challenges, the nuances of collaboration, and technical growth through senior feedback.
A reflection on the 10-week Boostcamp membership sprint: technical learning, design challenges, burnout, and lessons about using AI effectively.
A personal retrospective on the Boostcamp Web·Mobile Challenge phase: daily missions, CS learning, peer feedback, teamwork, and how I learned to grow alongside AI
A record of exploring the pros and cons of functional programming and object-oriented programming, and the reasons for choosing functional programming in React
My experience preparing for Naver Boostcamp Web·Mobile 10, completing the Basic course, and taking the problem-solving test.
A summary of my contributions to the Githru project during the OSCCA master phase, including UI improvements, issues, and Pull Requests.
My experience during the OSCCA challenge phase, including raising issues and making first Pull Request while exploring the Githru project.
My experience applying to the Open Source Contribution Academy and exploring the Githru VSCode Extension before contributing.
마지막 아티클까지 모두 확인했습니다.