← Назад к вопросам
Какой может быть побочный эффект в useEffect помимо запроса на сервер?
2.0 Middle🔥 291 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Побочные эффекты в useEffect
Побочный эффект (side effect) — это любое действие, которое влияет на что-либо вне компонента или его чистых функций. useEffect в React используется не только для запросов на сервер, но и для множества других побочных эффектов.
Примеры побочных эффектов
1. Работа с DOM напрямую
import { useEffect, useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// Побочный эффект: манипуляция DOM
inputRef.current?.focus();
inputRef.current?.select();
}, []);
return <input ref={inputRef} defaultValue="Focus me" />;
}
2. Управление локальным хранилищем (localStorage/sessionStorage)
function UserPreferences() {
const [theme, setTheme] = useState('light');
// Побочный эффект: запись в localStorage
useEffect(() => {
localStorage.setItem('theme', theme);
}, [theme]);
// Побочный эффект: чтение из localStorage
useEffect(() => {
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
}
}, []);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme: {theme}
</button>
);
}
3. Подписка на события (Event Listeners)
function WindowResizeListener() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
// Побочный эффект: добавление слушателя события
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
// Cleanup функция: удаление слушателя
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return <p>Window size: {windowSize.width}x{windowSize.height}</p>;
}
4. Работа с таймерами (setTimeout, setInterval)
function StopWatch() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
if (!isRunning) return;
// Побочный эффект: запуск интервала
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// Cleanup: очистка интервала
return () => clearInterval(interval);
}, [isRunning]);
return (
<div>
<p>Time: {seconds}s</p>
<button onClick={() => setIsRunning(!isRunning)}>
{isRunning ? 'Stop' : 'Start'}
</button>
</div>
);
}
5. Работа с Geolocation API
function LocationTracker() {
const [location, setLocation] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
// Побочный эффект: запрос текущего местоположения
if (!navigator.geolocation) {
setError('Geolocation not supported');
return;
}
navigator.geolocation.getCurrentPosition(
position => {
setLocation({
lat: position.coords.latitude,
lng: position.coords.longitude
});
},
error => setError(error.message)
);
}, []);
return location ? (
<p>Location: {location.lat}, {location.lng}</p>
) : (
<p>{error || 'Getting location...'}</p>
);
}
6. Подписка на Observable или EventEmitter
import { Subject } from 'rxjs';
const messageSubject = new Subject();
function MessageSubscriber() {
const [messages, setMessages] = useState([]);
useEffect(() => {
// Побочный эффект: подписка на Observable
const subscription = messageSubject.subscribe(message => {
setMessages(prev => [...prev, message]);
});
// Cleanup: отписка
return () => subscription.unsubscribe();
}, []);
return (
<ul>
{messages.map((msg, i) => <li key={i}>{msg}</li>)}
</ul>
);
}
7. Работа с медиа (Camera, Microphone)
function VideoStream() {
const videoRef = useRef(null);
const [error, setError] = useState(null);
useEffect(() => {
// Побочный эффект: запрос доступа к камере
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true
});
if (videoRef.current) {
videoRef.current.srcObject = stream;
}
} catch (err) {
setError(err.message);
}
}
startCamera();
// Cleanup: остановка потока
return () => {
if (videoRef.current?.srcObject) {
videoRef.current.srcObject
.getTracks()
.forEach(track => track.stop());
}
};
}, []);
return error ? <p>{error}</p> : <video ref={videoRef} autoPlay />;
}
8. Изменение документа или класса body
function DarkModeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// Побочный эффект: изменение класса на body
if (isDark) {
document.body.classList.add('dark-mode');
document.title = 'Dark Mode';
} else {
document.body.classList.remove('dark-mode');
document.title = 'Light Mode';
}
}, [isDark]);
return (
<button onClick={() => setIsDark(!isDark)}>
{isDark ? 'Light' : 'Dark'}
</button>
);
}
9. Работа с IndexedDB
function DatabaseCache() {
const [data, setData] = useState(null);
useEffect(() => {
// Побочный эффект: работа с IndexedDB
const openRequest = indexedDB.open('MyDB', 1);
openRequest.onsuccess = () => {
const db = openRequest.result;
const transaction = db.transaction('items');
const store = transaction.objectStore('items');
const request = store.getAll();
request.onsuccess = () => {
setData(request.result);
};
};
}, []);
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}
10. Аналитика и трекинг
function PageTracker() {
useEffect(() => {
// Побочный эффект: отправка аналитики
window.gtag?.('event', 'page_view', {
page_path: window.location.pathname
});
}, []);
return <div>Page loaded</div>;
}
11. Синхронизация между вкладками
function TabSync() {
const [value, setValue] = useState('');
useEffect(() => {
// Побочный эффект: слушание событий от других вкладок
const handleStorage = (e) => {
if (e.key === 'sharedValue') {
setValue(e.newValue || '');
}
};
window.addEventListener('storage', handleStorage);
return () => window.removeEventListener('storage', handleStorage);
}, []);
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
localStorage.setItem('sharedValue', newValue);
};
return <input value={value} onChange={handleChange} />;
}
Правила для побочных эффектов
1. Всегда очищай за собой (Cleanup)
useEffect(() => {
// Setup
const subscription = source.subscribe(handler);
const timer = setInterval(tick, 1000);
window.addEventListener('click', handler);
// Cleanup функция
return () => {
subscription.unsubscribe();
clearInterval(timer);
window.removeEventListener('click', handler);
};
}, []);
2. Управляй зависимостями
// Выполнится один раз при монтировании
useEffect(() => {
console.log('Mounted');
}, []);
// Выполнится когда변경 userId
useEffect(() => {
fetchUser(userId);
}, [userId]);
// Выполнится после каждого рендера
useEffect(() => {
console.log('Re-rendered');
});
Заключение
Побочные эффекты в useEffect — это не только запросы на сервер. Это работа с DOM, localStorage, событиями, таймерами, геолокацией, медиа, аналитикой и многим другим. Ключевой момент — всегда очищай ресурсы в cleanup функции, чтобы избежать утечек памяти и неправильного поведения приложения. Правильное управление побочными эффектами — это основа стабильного и производительного React приложения.