Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Hook useSelector (Redux)
useSelector — это хук из React Redux для чтения данных из Redux store. Это один из самых часто используемых хуков при работе с Redux.
Что такое useSelector
useSelector — это функция, которая позволяет компоненту подписаться на данные из Redux store и автоматически перерендериться при изменении этих данных.
import { useSelector } from 'react-redux';
function UserProfile() {
// Получаем userId из Redux store
const userId = useSelector((state) => state.auth.userId);
return <div>User ID: {userId}</div>;
}
Как это работает
Без Redux (prop drilling):
// App.js
function App() {
const [user, setUser] = useState({ id: 123, name: 'John' });
return <Header user={user} />;
}
// Header.js
function Header({ user }) {
return <UserInfo user={user} />;
}
// UserInfo.js
function UserInfo({ user }) {
return <div>{user.name}</div>;
}
// Проблема: user проходит через 3+ уровня компонентов!
С Redux (useSelector):
// UserInfo.js (прямой доступ без props)
function UserInfo() {
const user = useSelector((state) => state.auth.user);
return <div>{user.name}</div>;
}
Синтаксис useSelector
const value = useSelector(selector, equalityFn?);
Параметры:
selector— функция, которая извлекает данные из stateequalityFn— опциональная функция для проверки равенства (по умолчанию ===)
Примеры
Пример 1: Простое извлечение
import { useSelector } from 'react-redux';
function Dashboard() {
// Redux state:
// { user: { id: 1, name: 'John', email: 'john@example.com' } }
const userId = useSelector((state) => state.user.id);
const userName = useSelector((state) => state.user.name);
return (
<div>
<h1>{userName}</h1>
<p>ID: {userId}</p>
</div>
);
}
Пример 2: Получение целого объекта
function UserProfile() {
// Получить весь user объект
const user = useSelector((state) => state.user);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Пример 3: Вычисления в selector
function Cart() {
// Вычислить сумму в selector
const total = useSelector((state) => {
return state.cart.items.reduce(
(sum, item) => sum + (item.price * item.quantity),
0
);
});
return <div>Total: ${total}</div>;
}
Пример 4: Условное получение
function AuthorizedContent() {
const isAdmin = useSelector((state) => state.auth.user?.role === 'admin');
if (!isAdmin) return <div>Access denied</div>;
return <AdminPanel />;
}
Оптимизация: Memoization
Проблема: излишние перерендеры
function UserList() {
// Это создаёт НОВЫЙ массив при каждом вызове selector!
const users = useSelector((state) => state.users.filter(u => u.active));
// Компонент перерендеривается каждый раз,
// даже если actual данные не изменились!
console.log('Rendered'); // выведется много раз
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
Решение 1: useCallback
function UserList() {
const users = useSelector(
// Мемоизируем selector
useCallback(
(state) => state.users.filter(u => u.active),
[]
)
);
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
Решение 2: Reselect (рекомендуется)
import { createSelector } from 'reselect';
// Мемоизированный selector
const selectActiveUsers = createSelector(
(state) => state.users,
(users) => users.filter(u => u.active)
);
function UserList() {
const users = useSelector(selectActiveUsers);
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
Решение 3: Shallow equal
import { shallowEqual, useSelector } from 'react-redux';
function UserInfo() {
// Используем shallow equality для объектов
const { name, email } = useSelector(
(state) => ({
name: state.user.name,
email: state.user.email
}),
shallowEqual // не перерендер если значения внутри одинаковые
);
return (
<div>
<p>{name}</p>
<p>{email}</p>
</div>
);
}
Redux state структура (best practices)
// Хорошая структура для useSelector
const state = {
auth: {
isLoading: false,
user: null,
error: null
},
users: {
list: [],
selectedId: null
},
posts: {
byId: { '1': {...}, '2': {...} },
allIds: ['1', '2']
}
};
// Селекторы (отдельный файл)
export const selectIsAuthenticated = (state) => state.auth.user !== null;
export const selectCurrentUser = (state) => state.auth.user;
export const selectAllUsers = (state) => state.users.list;
export const selectUserById = (state, userId) =>
state.users.list.find(u => u.id === userId);
Практический пример с действиями
// Redux store
const initialState = {
user: { id: null, name: null, role: null },
isLoading: false,
error: null
};
const authReducer = (state = initialState, action) => {
switch(action.type) {
case 'LOGIN_START':
return { ...state, isLoading: true };
case 'LOGIN_SUCCESS':
return { ...state, user: action.payload, isLoading: false };
case 'LOGIN_ERROR':
return { ...state, error: action.payload, isLoading: false };
default: return state;
}
};
// Компонент с useSelector
function LoginForm() {
const dispatch = useDispatch();
const isLoading = useSelector((state) => state.isLoading);
const error = useSelector((state) => state.error);
const user = useSelector((state) => state.user);
const handleLogin = (credentials) => {
dispatch({ type: 'LOGIN_START' });
// API call...
dispatch({ type: 'LOGIN_SUCCESS', payload: userData });
};
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (user.id) return <div>Welcome, {user.name}!</div>;
return <form onSubmit={handleLogin}>...</form>;
}
Сравнение с альтернативами
// 1. useSelector (Redux)
const user = useSelector((state) => state.user);
// 2. Context API
const user = useContext(UserContext);
// 3. Props
<Component user={user} />
// Рекомендуется:
// - Redux для больших приложений
// - Context для небольших
// - Props для локального состояния
Частые ошибки
Ошибка 1: Создание нового объекта в selector
// ❌ ПЛОХО: новый объект при каждом вызове
const user = useSelector((state) => ({
name: state.user.name,
email: state.user.email
}));
// Компонент перерендеривается всегда!
// ✅ ХОРОШО: используй shallowEqual
const user = useSelector(
(state) => ({
name: state.user.name,
email: state.user.email
}),
shallowEqual
);
Ошибка 2: Сложные вычисления в selector
// ❌ ПЛОХО: тяжелые вычисления
const expensiveValue = useSelector((state) => {
return state.items.reduce((acc, item) => {
// сложные вычисления...
}, {});
});
// ✅ ХОРОШО: используй createSelector
const selectExpensiveValue = createSelector(
(state) => state.items,
(items) => items.reduce(...)
);
const expensiveValue = useSelector(selectExpensiveValue);
Ошибка 3: useSelector в циклах
// ❌ ПЛОХО: нарушает правила хуков
function UsersList() {
for (let i = 0; i < 10; i++) {
const user = useSelector((state) => state.users[i]); // error!
}
}
// ✅ ХОРОШО: вызови useSelector один раз
function UsersList() {
const users = useSelector((state) => state.users);
return users.map((user) => <User key={user.id} user={user} />);
}
Итого: useSelector
Что это: Хук для чтения данных из Redux store
Назначение:
- Избежать prop drilling
- Автоматическое перерендеривание при изменении данных
- Селектора для извлечения конкретных данных
Использование:
const data = useSelector((state) => state.path.to.data);
Оптимизация:
- createSelector (Reselect)
- shallowEqual
- Мемоизированные селекторы
Правила:
- Один selector per использование
- Мемоизируй сложные вычисления
- Используй объекты state для структурирования
useSelector — это основной способ подключения компонентов к Redux store.