← Назад к вопросам

Как достать состояние из RTK reducer?

2.0 Middle🔥 131 комментариев
#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Получение состояния из RTK (Redux Toolkit) Reducer

Вопрос о работе с Redux Toolkit. Покажу различные способы получения состояния из редьюсера.

Что такое Redux Toolkit?

RTK - это современный способ работы с Redux. Упрощает создание:

  • Reducers (редьюсеры)
  • Actions (действия)
  • Store (хранилище)
  • Selectors (селекторы)

1. Создание slice редьюсера

Сначала создаем slice с состоянием:

import { createSlice } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    profile: {
      id: 1,
      name: 'John',
      email: 'john@example.com'
    },
    isLoading: false,
    error: null
  },
  reducers: {
    setUser: (state, action) => {
      state.profile = action.payload;
    },
    setLoading: (state, action) => {
      state.isLoading = action.payload;
    }
  }
});

export const { setUser, setLoading } = userSlice.actions;
export default userSlice.reducer;

2. Настройка Store

import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
import postsReducer from './postsSlice';

export const store = configureStore({
  reducer: {
    user: userReducer,
    posts: postsReducer
  }
});

3. Получение состояния - основные способы

Способ 1: useSelector (основной в функциональных компонентах)

import { useSelector } from 'react-redux';

function UserProfile() {
  // Получаем весь объект user
  const user = useSelector(state => state.user);
  
  // Получаем только profile
  const profile = useSelector(state => state.user.profile);
  
  // Получаем только name
  const name = useSelector(state => state.user.profile.name);
  
  return <h1>{name}</h1>;
}

Способ 2: useSelector с селектором (лучшая практика)

// Сначала создаем селекторы
export const selectUser = (state) => state.user;
export const selectProfile = (state) => state.user.profile;
export const selectUserName = (state) => state.user.profile.name;
export const selectIsLoading = (state) => state.user.isLoading;

// Потом используем в компонентах
function UserProfile() {
  const profile = useSelector(selectProfile);
  const isLoading = useSelector(selectIsLoading);
  
  if (isLoading) return <div>Загрузка...</div>;
  
  return <h1>{profile.name}</h1>;
}

Способ 3: Использование reselect для мемоизации

import { createSelector } from '@reduxjs/toolkit';

// Мемоизируем селектор (вызывается только если значение изменилось)
export const selectUserInitials = createSelector(
  state => state.user.profile,
  profile => profile.name
    .split(' ')
    .map(n => n[0])
    .join('')
);

export const selectUserFullInfo = createSelector(
  state => state.user.profile,
  state => state.user.isLoading,
  (profile, isLoading) => ({
    ...profile,
    isLoading
  })
);

// В компоненте
function UserCard() {
  const initials = useSelector(selectUserInitials);
  const fullInfo = useSelector(selectUserFullInfo);
  
  return (
    <div>
      <span>{initials}</span>
      <p>{fullInfo.name}</p>
    </div>
  );
}

4. Получение в классовых компонентах

Способ 1: connect (старый стиль, но еще используется)

import { connect } from 'react-redux';

class UserProfile extends React.Component {
  render() {
    const { user, profile, isLoading } = this.props;
    
    return <h1>{profile.name}</h1>;
  }
}

// Маппим состояние на props
const mapStateToProps = (state) => ({
  user: state.user,
  profile: state.user.profile,
  isLoading: state.user.isLoading
});

export default connect(mapStateToProps)(UserProfile);

Способ 2: useSelector в функциональном компоненте (рекомендуется)

import { useSelector } from 'react-redux';
import { selectProfile, selectIsLoading } from './userSlice';

function UserProfile() {
  const profile = useSelector(selectProfile);
  const isLoading = useSelector(selectIsLoading);
  
  return (
    <div>
      {isLoading ? <p>Загрузка...</p> : <h1>{profile.name}</h1>}
    </div>
  );
}

5. Получение с фильтрацией (createSelector)

import { createSelector } from '@reduxjs/toolkit';

// Создаем селектор для постов пользователя
export const selectUserPosts = createSelector(
  state => state.posts.items,
  state => state.user.profile.id,
  (posts, userId) => posts.filter(post => post.userId === userId)
);

function UserPosts() {
  const posts = useSelector(selectUserPosts);
  
  return (
    <ul>
      {posts.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

6. Оптимизация: избегай глубокого поиска в каждом рендере

// ПЛОХО: этот селектор создает новый объект каждый раз
function BadExample() {
  const user = useSelector(state => ({
    name: state.user.profile.name,
    email: state.user.profile.email
  }));
}

// ХОРОШО: используем мемоизированный селектор
export const selectUserInfo = createSelector(
  state => state.user.profile,
  profile => ({
    name: profile.name,
    email: profile.email
  })
);

function GoodExample() {
  const user = useSelector(selectUserInfo);
}

7. Асинхронные операции (createAsyncThunk)

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// Создаем асинхронное действие
export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  }
);

const userSlice = createSlice({
  name: 'user',
  initialState: {
    profile: null,
    loading: false,
    error: null
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.profile = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  }
});

// Использование в компоненте
function UserLoader() {
  const dispatch = useDispatch();
  const { profile, loading, error } = useSelector(state => state.user);
  
  useEffect(() => {
    dispatch(fetchUser(1));
  }, [dispatch]);
  
  if (loading) return <p>Загрузка...</p>;
  if (error) return <p>Ошибка: {error}</p>;
  
  return <h1>{profile?.name}</h1>;
}

8. Структурированный пример (лучшая практика)

// userSlice.js
import { createSlice, createSelector } from '@reduxjs/toolkit';

const userSlice = createSlice({
  name: 'user',
  initialState: {
    profile: null,
    preferences: {},
    isLoading: false
  },
  reducers: {
    setProfile: (state, action) => {
      state.profile = action.payload;
    }
  }
});

// Селекторы (экспортируем)
export const selectUser = (state) => state.user;
export const selectProfile = (state) => state.user.profile;
export const selectIsLoading = (state) => state.user.isLoading;

export const selectUserDisplayName = createSelector(
  selectProfile,
  profile => profile?.name || 'Anonymous'
);

export default userSlice.reducer;

// UserProfile.jsx
import { useSelector } from 'react-redux';
import { selectProfile, selectUserDisplayName, selectIsLoading } from './userSlice';

function UserProfile() {
  const profile = useSelector(selectProfile);
  const displayName = useSelector(selectUserDisplayName);
  const isLoading = useSelector(selectIsLoading);
  
  return (
    <div>
      {isLoading && <p>Загрузка...</p>}
      {profile && (
        <div>
          <h1>{displayName}</h1>
          <p>{profile.email}</p>
        </div>
      )}
    </div>
  );
}

Выводы

Как получить состояние из RTK reducer:

  1. useSelector - основной хук для функциональных компонентов
  2. Создавай селекторы - храни их в том же файле slice
  3. Используй createSelector - для мемоизации и производительности
  4. Избегай анонимных селекторов - создавай именованные функции
  5. Логика фильтрации в селекторах - не в компонентах

Современный Redux Toolkit значительно упростил работу с состоянием по сравнению с обычным Redux.