# Redux Toolkit Setup ## Installation ```bash yarn add @reduxjs/toolkit react-redux redux-persist yarn add -D @types/react-redux ``` ## Store Configuration ```typescript // src/redux/store/index.ts import {configureStore} from '@reduxjs/toolkit'; import {persistStore, persistReducer} from 'redux-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; import {combineReducers} from '@reduxjs/toolkit'; import authSlice from '../slices/authSlice'; import userSlice from '../slices/userSlice'; const persistConfig = { key: 'root', storage: AsyncStorage, whitelist: ['auth'], }; const rootReducer = combineReducers({ auth: authSlice, user: userSlice, }); const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, middleware: getDefaultMiddleware => getDefaultMiddleware({ serializableCheck: { ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'], }, }), }); export const persistor = persistStore(store); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; ``` ## Auth Slice ```typescript // src/redux/slices/authSlice.ts import {createSlice, PayloadAction} from '@reduxjs/toolkit'; interface User { id: string; email: string; firstName: string; lastName: string; avatar?: string; } interface AuthState { isAuthenticated: boolean; user: User | null; token: string | null; refreshToken: string | null; loading: boolean; error: string | null; } const initialState: AuthState = { isAuthenticated: false, user: null, token: null, refreshToken: null, loading: false, error: null, }; const authSlice = createSlice({ name: 'auth', initialState, reducers: { loginStart: (state) => { state.loading = true; state.error = null; }, loginSuccess: (state, action: PayloadAction<{user: User; token: string; refreshToken: string}>) => { state.loading = false; state.isAuthenticated = true; state.user = action.payload.user; state.token = action.payload.token; state.refreshToken = action.payload.refreshToken; state.error = null; }, loginFailure: (state, action: PayloadAction) => { state.loading = false; state.isAuthenticated = false; state.user = null; state.token = null; state.refreshToken = null; state.error = action.payload; }, logout: (state) => { state.isAuthenticated = false; state.user = null; state.token = null; state.refreshToken = null; state.error = null; }, updateUser: (state, action: PayloadAction>) => { if (state.user) { state.user = {...state.user, ...action.payload}; } }, clearError: (state) => { state.error = null; }, }, }); export const { loginStart, loginSuccess, loginFailure, logout, updateUser, clearError, } = authSlice.actions; export default authSlice.reducer; ``` ## Typed Hooks ```typescript // src/redux/hooks.ts import {useDispatch, useSelector, TypedUseSelectorHook} from 'react-redux'; import type {RootState, AppDispatch} from './store'; export const useAppDispatch = () => useDispatch(); export const useAppSelector: TypedUseSelectorHook = useSelector; ``` ## Async Thunks ```typescript // src/redux/thunks/authThunks.ts import {createAsyncThunk} from '@reduxjs/toolkit'; import {authAPI} from '@services/authAPI'; interface LoginCredentials { email: string; password: string; } export const loginUser = createAsyncThunk( 'auth/loginUser', async (credentials: LoginCredentials, {rejectWithValue}) => { try { const response = await authAPI.login(credentials); return response.data; } catch (error: any) { return rejectWithValue(error.response?.data?.message || 'Login failed'); } } ); export const refreshToken = createAsyncThunk( 'auth/refreshToken', async (_, {getState, rejectWithValue}) => { try { const state = getState() as any; const refreshToken = state.auth.refreshToken; if (!refreshToken) { throw new Error('No refresh token available'); } const response = await authAPI.refreshToken(refreshToken); return response.data; } catch (error: any) { return rejectWithValue(error.response?.data?.message || 'Token refresh failed'); } } ); ``` ## Provider Setup ```typescript // App.tsx import React from 'react'; import {Provider} from 'react-redux'; import {PersistGate} from 'redux-persist/integration/react'; import {store, persistor} from './src/redux/store'; import {AppNavigator} from './src/navigation/AppNavigator'; import {LoadingScreen} from './src/components/common/LoadingScreen'; const App: React.FC = () => { return ( } persistor={persistor}> ); }; export default App; ``` ## Middleware Configuration ```typescript // src/redux/middleware/authMiddleware.ts import {Middleware} from '@reduxjs/toolkit'; import {logout} from '../slices/authSlice'; export const authMiddleware: Middleware = (store) => (next) => (action) => { // Handle token expiration if (action.type.endsWith('/rejected') && action.payload?.status === 401) { store.dispatch(logout()); } return next(action); }; ``` ## Selectors ```typescript // src/redux/selectors/authSelectors.ts import {createSelector} from '@reduxjs/toolkit'; import {RootState} from '../store'; export const selectAuth = (state: RootState) => state.auth; export const selectIsAuthenticated = createSelector( [selectAuth], (auth) => auth.isAuthenticated ); export const selectUser = createSelector( [selectAuth], (auth) => auth.user ); export const selectAuthLoading = createSelector( [selectAuth], (auth) => auth.loading ); export const selectAuthError = createSelector( [selectAuth], (auth) => auth.error ); export const selectUserFullName = createSelector( [selectUser], (user) => user ? `${user.firstName} ${user.lastName}` : '' ); ```