← Назад к вопросам
Как в useState сохранить первоначальное состояние чтобы компонент не перерендовался?
2.0 Middle🔥 261 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Сохранение первоначального состояния в useState без перерендеров
Это частая задача при разработке на React - нужно сохранить исходное состояние и избежать ненужных перерендеров. Есть несколько подходов.
Проблема: обновление с теми же значениями вызывает перерендер
function UserProfile() {
const [user, setUser] = useState({ name: 'John', age: 30 })
const [initialUser, setInitialUser] = useState({ name: 'John', age: 30 })
// Проблема: каждый раз создаются новые объекты
// Даже если значения одинаковые, это разные объекты в памяти
console.log('Рендер')
}
Браузер консоль выведет "Рендер" много раз.
Решение 1: Использование useRef для сохранения первоначального значения
useRef сохраняет значение между рендерами БЕЗ перерендера:
import { useState, useRef } from 'react'
function UserProfile() {
const [user, setUser] = useState({ name: 'John', age: 30 })
const initialUserRef = useRef({ name: 'John', age: 30 })
const handleReset = () => {
// Восстанавливаем исходное состояние
setUser(initialUserRef.current)
}
return (
<div>
<button onClick={handleReset}>Сбросить на исходное</button>
</div>
)
}
Ключевое отличие:
useStateвызывает перерендер при обновленииuseRefНЕ вызывает перерендер, просто хранит значение
Решение 2: Сохранение начального состояния через параметр
Используй функцию инициализации для избегания пересоздания:
function UserForm({ initialData }) {
// Плохо: initialData будет пересоздаваться
const [user, setUser] = useState(initialData)
// Хорошо: инициализируется только один раз
const [user, setUser] = useState(() => {
// Эта функция вызывается только при первом рендере
return { ...initialData }
})
const initialRef = useRef(initialData)
}
Решение 3: Сравнение объектов для избегания обновления
Проверяй, изменилось ли состояние, перед обновлением:
import { useState, useCallback } from 'react'
function UserForm() {
const [user, setUser] = useState({ name: 'John', age: 30 })
const [initialUser] = useState({ name: 'John', age: 30 })
const handleChange = useCallback((newUser) => {
// Только обновляй, если данные действительно изменились
if (JSON.stringify(newUser) !== JSON.stringify(user)) {
setUser(newUser)
}
}, [user])
return <input onChange={e => handleChange({ ...user, name: e.target.value })} />
}
Или более эффективно с deep comparison:
function deepEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2)
}
const handleChange = useCallback((newUser) => {
if (!deepEqual(newUser, user)) {
setUser(newUser)
}
}, [user])
Решение 4: Использование useMemo для сохранения объекта
import { useState, useMemo } from 'react'
function UserForm({ userData }) {
const [user, setUser] = useState(userData)
// Сохраняем начальное состояние в memoized переменной
const initialUser = useMemo(() => {
return { ...userData }
}, [userData])
const handleReset = () => {
setUser(initialUser)
}
return <button onClick={handleReset}>Сбросить</button>
}
Решение 5: Уменьшение состояния для избегания перерендеров
Вместо хранения всего объекта, храни только измененные значения:
function UserForm() {
const [changes, setChanges] = useState({}) // Только изменения
const initialUser = { name: 'John', age: 30 } // Константа, не состояние
const currentUser = { ...initialUser, ...changes } // Слияние
const handleChange = (field, value) => {
setChanges(prev => ({ ...prev, [field]: value }))
}
const handleReset = () => {
setChanges({}) // Просто очищаем изменения
}
return <button onClick={handleReset}>Сбросить</button>
}
Решение 6: useReducer для более сложного состояния
import { useReducer } from 'react'
const initialState = { name: 'John', age: 30 }
function reducer(state, action) {
switch (action.type) {
case 'CHANGE':
return { ...state, [action.field]: action.value }
case 'RESET':
return initialState // Восстанавливаем из константы
default:
return state
}
}
function UserForm() {
const [user, dispatch] = useReducer(reducer, initialState)
const handleReset = () => {
dispatch({ type: 'RESET' })
}
return <button onClick={handleReset}>Сбросить</button>
}
Практический пример: форма редактирования
import { useState, useRef } from 'react'
function EditUser({ user: initialUser }) {
const [user, setUser] = useState(initialUser)
const savedUserRef = useRef(initialUser)
const hasChanges = JSON.stringify(user) !== JSON.stringify(savedUserRef.current)
const handleChange = (field, value) => {
setUser(prev => ({ ...prev, [field]: value }))
}
const handleSave = async () => {
await fetch(`/api/users/${user.id}`, {
method: 'PUT',
body: JSON.stringify(user)
})
savedUserRef.current = user // Обновляем сохраненное значение
}
const handleReset = () => {
setUser(savedUserRef.current) // Восстанавливаем из сохраненного
}
return (
<div>
<input
value={user.name}
onChange={e => handleChange('name', e.target.value)}
/>
<button onClick={handleSave} disabled={!hasChanges}>Сохранить</button>
<button onClick={handleReset} disabled={!hasChanges}>Отменить</button>
</div>
)
}
Когда использовать какой подход
| Сценарий | Решение | Причина |
|---|---|---|
| Сохранить исходное для reset | useRef | Не вызовет перерендер |
| Инициализация сложная | useState(() => {}) | Функция вызывается только один раз |
| Избежать обновления если значение же | Сравнение перед setState | Экономит перерендер |
| Много полей в форме | useReducer | Более организованный код |
| Нужны только изменения | Хранить только changes | Меньше данных в состоянии |
Важная деталь: Object.freeze для защиты
const initialUser = Object.freeze({ name: 'John', age: 30 })
function UserForm() {
const [user, setUser] = useState(initialUser)
// initialUser больше не может быть случайно изменен
}
Резюме
Для сохранения первоначального состояния без перерендеров:
- Используй useRef если не нужен перерендер
- Используй мемоизированное значение если нужно отслеживать изменения
- Храни только изменения вместо всего состояния
- Используй useReducer для сложной логики
- Сравнивай значения перед
setStateесли нужно экономить перерендеры
Право выбора зависит от того, нужен ли тебе перерендер при восстановлении исходного состояния или нет.