← Назад к вопросам
Как сохранить предыдущие Props компонента с помощью UseRef?
1.8 Middle🔥 61 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как сохранить предыдущие Props компонента с помощью useRef
useRef позволяет хранить значение, которое персистирует между рендерами, но не вызывает перерендер при изменении. Это идеально подходит для отслеживания предыдущих значений props.
Задача: Отследить изменения Props
Зачем это нужно?
- Сравнить новые и старые значения
- Выполнить действие только когда prop изменился
- Отследить историю изменений
Способ 1: Сохранение одного prop
import { useRef, useEffect } from 'react';
function Counter({ count }) {
const prevCountRef = useRef();
useEffect(() => {
// После рендера сохраняем текущее значение
prevCountRef.current = count;
}, [count]);
return (
<div>
<p>Текущее значение: {count}</p>
<p>Предыдущее значение: {prevCountRef.current}</p>
</div>
);
}
Как это работает:
- При первом рендере
prevCountRef.currentundefined - После рендера useEffect сохраняет текущее значение count
- При изменении count компонент перерендеривается
- useEffect обновляет ref на новое значение
Способ 2: Создание пользовательского хука usePrevious
Удобный вспомогательный хук:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
function Component({ userId }) {
const prevUserId = usePrevious(userId);
return (
<div>
<p>Текущий ID: {userId}</p>
<p>Предыдущий ID: {prevUserId}</p>
</div>
);
}
Способ 3: Сохранение нескольких props
import { useRef, useEffect } from 'react';
function User({ name, email, age }) {
const prevPropsRef = useRef();
useEffect(() => {
// Сохраняем все предыдущие props
prevPropsRef.current = { name, email, age };
}, [name, email, age]);
const prevProps = prevPropsRef.current;
return (
<div>
<p>Имя: {name} (было {prevProps?.name})</p>
<p>Email: {email} (было {prevProps?.email})</p>
<p>Возраст: {age} (было {prevProps?.age})</p>
</div>
);
}
Способ 4: Обнаружение изменения конкретного prop
function UserProfile({ userId, userName, isActive }) {
const prevUserIdRef = useRef();
useEffect(() => {
if (prevUserIdRef.current !== userId) {
console.log('User ID изменился!');
console.log(`Было: ${prevUserIdRef.current}, стало: ${userId}`);
}
prevUserIdRef.current = userId;
}, [userId]);
return (
<div>
<p>User ID: {userId}</p>
<p>User Name: {userName}</p>
<p>Active: {isActive ? 'Yes' : 'No'}</p>
</div>
);
}
Способ 5: Отслеживание предыдущих значений с логированием
function DataDisplay({ data, status }) {
const prevDataRef = useRef();
const prevStatusRef = useRef();
useEffect(() => {
const didDataChange = prevDataRef.current !== data;
const didStatusChange = prevStatusRef.current !== status;
if (didDataChange) {
console.log('Данные обновились:', {
prev: prevDataRef.current,
current: data
});
}
if (didStatusChange) {
console.log('Статус изменился:', {
prev: prevStatusRef.current,
current: status
});
}
prevDataRef.current = data;
prevStatusRef.current = status;
}, [data, status]);
return (
<div>
<p>Data: {JSON.stringify(data)}</p>
<p>Status: {status}</p>
</div>
);
}
Способ 6: Сравнение объектов в props
function UserCard({ user }) {
const prevUserRef = useRef();
useEffect(() => {
if (JSON.stringify(prevUserRef.current) !== JSON.stringify(user)) {
console.log('Пользователь изменился');
}
prevUserRef.current = user;
}, [user]);
return (
<div>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}
Способ 7: Отслеживание истории изменений
function ComponentWithHistory({ value }) {
const historyRef = useRef([]);
useEffect(() => {
// Добавляем новое значение в историю
historyRef.current.push({
value,
timestamp: new Date()
});
// Ограничиваем историю последними 10 изменениями
if (historyRef.current.length > 10) {
historyRef.current.shift();
}
}, [value]);
const handleShowHistory = () => {
console.log('История изменений:', historyRef.current);
};
return (
<div>
<p>Текущее значение: {value}</p>
<button onClick={handleShowHistory}>Показать историю</button>
</div>
);
}
Способ 8: Сравнение глубоких изменений
function ComplexComponent({ data }) {
const prevDataRef = useRef(data);
useEffect(() => {
const hasChanged = JSON.stringify(prevDataRef.current) !== JSON.stringify(data);
if (hasChanged) {
// Выполняем действие при изменении данных
fetchMoreData(data);
}
prevDataRef.current = data;
}, [data]);
const fetchMoreData = (data) => {
console.log('Получаем дополнительные данные для:', data);
};
return <div>{JSON.stringify(data)}</div>;
}
Способ 9: Использование ref для условного выполнения useEffect
function FormComponent({ formData, isSubmitted }) {
const isFirstRenderRef = useRef(true);
useEffect(() => {
// Пропускаем первый рендер
if (isFirstRenderRef.current) {
isFirstRenderRef.current = false;
return;
}
// Выполняем действие только при изменении после первого рендера
console.log('Форма изменена (не первый рендер):', formData);
}, [formData]);
return (
<div>
<p>Заполненные данные: {JSON.stringify(formData)}</p>
</div>
);
}
Способ 10: Отслеживание с callback при изменении
function MonitoredComponent({ userId, onUserChange }) {
const prevUserIdRef = useRef();
useEffect(() => {
if (prevUserIdRef.current && prevUserIdRef.current !== userId) {
// Вызываем callback при изменении userId
onUserChange({
prevUserId: prevUserIdRef.current,
newUserId: userId
});
}
prevUserIdRef.current = userId;
}, [userId, onUserChange]);
return <div>User ID: {userId}</div>;
}
// Использование
<MonitoredComponent
userId={5}
onUserChange={(change) => console.log('Смена пользователя:', change)}
/>
Сравнение: useRef vs useState
useRef для предыдущего значения:
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]);
// Не вызывает перерендер
useState для предыдущего значения:
const [prevCount, setPrevCount] = useState();
useEffect(() => {
setPrevCount(count);
}, [count]);
// Вызывает дополнительный перерендер
Практический пример: Отслеживание изменения user ID
function useUserData(userId) {
const [userData, setUserData] = useState(null);
const prevUserIdRef = useRef();
useEffect(() => {
// Выполняем запрос только если userId действительно изменился
if (prevUserIdRef.current !== userId) {
fetchUser(userId).then(setUserData);
prevUserIdRef.current = userId;
}
}, [userId]);
return userData;
}
function UserDetail({ userId }) {
const userData = useUserData(userId);
return (
<div>
{userData && (
<>
<h2>{userData.name}</h2>
<p>{userData.email}</p>
</>
)}
</div>
);
}
Выводы
- useRef сохраняет значение между рендерами без перерендера
- Используй useEffect для обновления ref после каждого рендера
- Создавай usePrevious хук для переиспользования логики
- Сравнивай предыдущие и текущие значения для обнаружения изменений
- useRef идеален для отслеживания истории и условного выполнения логики
- Не забывай обновлять ref в useEffect, иначе он всегда будет содержать старое значение