React performance in 2025 is becoming increasingly common in day-to-day development. This article offers a systematic look at its usage, inner workings, and optimization strategies.
Core Principles
Let's start by looking at the basic implementation:
import { useState, useEffect, useCallback, useMemo } from "react";
function useDataFetching(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
try {
setLoading(true);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
const json = await res.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
This snippet illustrates the fundamental usage. In real projects you'll also need to account for error handling and edge cases.
Advanced Features
Building on this foundation, we can further optimize:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === "largest-contentful-paint") {
reportMetric("LCP", entry.startTime);
}
if (entry.entryType === "first-input") {
reportMetric("FID", entry.processingStart - entry.startTime);
}
}
});
observer.observe({ entryTypes: ["largest-contentful-paint", "first-input"] });
This approach improves both the testability and scalability of the code.
Project Practice
In a real project, the usage gets a bit more complex:
import { useRef, useEffect, useState } from "react";
function useIntersectionObserver(options = {}) {
const [isVisible, setIsVisible] = useState(false);
const ref = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting);
},
{ threshold: 0.1, ...options },
);
const el = ref.current;
if (el) observer.observe(el);
return () => {
if (el) observer.unobserve(el);
};
}, []);
return [ref, isVisible];
}
Pay attention to edge-case handling—this is crucial in production environments.
Best Practices
Here is a complete example:
import { useReducer, useCallback } from "react";
const initialState = { items: [], filter: "", sort: "date" };
function reducer(state, action) {
switch (action.type) {
case "SET_ITEMS":
return { ...state, items: action.payload };
case "SET_FILTER":
return { ...state, filter: action.payload };
case "ADD_ITEM":
return { ...state, items: [...state.items, action.payload] };
case "REMOVE_ITEM":
return {
...state,
items: state.items.filter((i) => i.id !== action.payload),
};
default:
throw new Error(`Unknown: ${action.type}`);
}
}
Performance optimization must be tailored to specific scenarios—not every situation calls for aggressive optimization.
Summary
- Always verify compatibility before using in production
- In team collaboration, conventions and documentation matter more than the technology itself
- Stay up-to-date with community trends; technical solutions require continuous iteration