Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что хранить в state
Нет, не всё. Хранение всего в state — это anti-pattern, который делает приложение медленным и сложным. Нужно критически подходить к тому, что живёт в state.
Правило 80/20
В state хранить только:
- Данные, которые используют несколько компонентов
- Данные, изменяющиеся часто (и требующие перерендеринга)
- UI состояние (opened, selected, loading, error)
НЕ хранить в state:
- Статические данные (конфиг, константы)
- Данные, нужные одному компоненту (local state)
- Производные данные (computed/selector)
- Кэширующиеся данные (используй хуки типа useMemo)
- Временные значения (в процессе редактирования)
Примеры НЕправильного подхода
// ПЛОХО — всё в Redux
const store = {
users: [...],
posts: [...],
comments: [...],
currentUser: {...},
currentPost: {...},
currentComment: {...},
theme: 'light',
language: 'en',
isLoading: false,
error: null,
isMenuOpen: false,
isModalOpen: false,
isDropdownOpen: false,
notifications: [...],
unreadCount: 0,
lastUpdated: 1234567890,
// ... ещё 50 полей
};
Проблемы:
- Redux store слишком большой
- Любое изменение = перерендеринг всех компонентов
- Сложно отследить, что где используется
- Медленная девелопер опытность
Правильный подход
// Redux/Vuex — только shared state
const store = {
users: { byId: {...}, allIds: [...] }, // Используют несколько компонентов
currentUserId: '123', // Нужен многим компонентам
theme: 'light', // Глобальное состояние
isAuthenticated: true, // Критично для приложения
};
// Компонент со своим state
function UserProfile() {
const [editMode, setEditMode] = useState(false); // Локальное состояние
const [formData, setFormData] = useState({}); // Локальное
const currentUser = useSelector(state =>
state.users.byId[state.currentUserId] // Только читаем из store
);
return (
<div>
{editMode ? (
<EditForm data={formData} /> // formData НЕ в store
) : (
<ProfileCard user={currentUser} />
)}
</div>
);
}
Иерархия где хранить данные
1. Локальный state (useState) — 70% данных
function SearchForm() {
const [query, setQuery] = useState(''); // Не нужен другим компонентам
const [filters, setFilters] = useState({}); // Локальное
const handleSearch = () => {
// Отправляем на сервер или в Redux ТОЛЬКО результат
};
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<button onClick={handleSearch}>Search</button>
</div>
);
}
2. Контекст (Context API) — 20% данных
// Для данных используемых всем поддеревом
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// Используется:
function DarkModeToggle() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle {theme}
</button>
);
}
3. Redux/Vuex/Pinia — 10% данных
// Только действительно глобальные данные
const store = {
auth: {
user: {...},
isAuthenticated: true,
token: '...',
},
notifications: [...],
};
Производные данные — НЕ в state
// ПЛОХО
const store = {
users: [{id: 1, name: 'John'}, {id: 2, name: 'Jane'}],
userCount: 2, // Это производное, не храни!
userNames: ['John', 'Jane'], // Тоже производное
};
// ХОРОШО
const users = useSelector(state => state.users);
const userCount = useMemo(() => users.length, [users]); // Вычисляем
const userNames = useMemo(() => users.map(u => u.name), [users]);
Сложные состояния компонента
// ПЛОХО — в Redux
const store = {
isDropdownOpen: false,
isTooltipOpen: false,
isMenuOpen: false,
// ... 10 булевых флагов
};
// ХОРОШО — в компоненте
function Dropdown() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && <Menu />}
</div>
);
}
Временные данные при редактировании
// ПЛОХО
const store = {
user: {...},
editingUserData: {...}, // Временное, меняется каждый keystroke
};
// ХОРОШО
function UserEditForm() {
const user = useSelector(state => state.user); // Из store
const [formData, setFormData] = useState(user); // Локальное копирование
const handleSave = async () => {
await updateUser(formData);
// dispatch(setUser(formData)); // Потом синхронизируем
};
return (
<form>
<input
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
/>
<button onClick={handleSave}>Save</button>
</form>
);
}
Оптимизация: selectors
// Для больших деревьев state, используй selectors
const selectCurrentUser = state =>
state.users.byId[state.currentUserId];
const selectCurrentUserComments = state => {
const user = selectCurrentUser(state);
return state.comments.filter(c => c.userId === user.id);
};
// Или с Reselect для memoization
import { createSelector } from 'reselect';
const selectUsers = state => state.users;
const selectUserIds = state => state.userIds;
const selectUsersByIdSorted = createSelector(
[selectUsers, selectUserIds],
(users, ids) => ids.map(id => users[id])
);
Кэширование API запросов
// НЕ храни всё в state, используй custom hook
function useUser(userId) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const cacheRef = useRef({});
useEffect(() => {
if (cacheRef.current[userId]) {
setData(cacheRef.current[userId]);
return;
}
setLoading(true);
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(user => {
cacheRef.current[userId] = user;
setData(user);
})
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
return { data, loading, error };
}
Правило большого пальца
Спроси себя перед добавлением в state:
- Это нужно нескольким компонентам? Если нет → локальный state
- Это меняется часто? Если редко → переменная, не state
- Это производное от другого state? Если да → useMemo, не отдельный state
- Это действительно глобальное? Если нет → контекст вместо Redux
- Это влияет на UX? Если нет → не в state
Вывод
Правило 80/20:
- 80% → Локальный state в компонентах
- 15% → Context для локальных деревьев
- 5% → Redux/Vuex для действительно глобального state
Преимущества такого подхода:
- Быстрое приложение (меньше перерендеринга)
- Простой и понятный код
- Легко отследить где что используется
- Меньше багов (меньше синхронизации)