import {
configureStore,
createSlice,
PayloadAction,
createAsyncThunk,
combineReducers,
} from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
// ============================================
// Types
// ============================================
interface User {
id: string;
name: string;
email: string;
}
interface AuthState {
user: User | null;
token: string | null;
status: "idle" | "loading" | "succeeded" | "failed";
error: string | null;
}
interface CounterState {
value: number;
}
// ============================================
// Async Thunks
// ============================================
export const loginUser = createAsyncThunk(
"auth/login",
async (credentials: { email: string }, { rejectWithValue }) => {
try {
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1000));
if (credentials.email === "error@example.com")
throw new Error("Login failed");
return {
user: { id: "1", name: "John Doe", email: credentials.email },
token: "fake-jwt-token",
};
} catch (error: any) {
return rejectWithValue(error.message);
}
},
);
// ============================================
// Slices
// ============================================
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
token: null,
status: "idle",
error: null,
} as AuthState,
reducers: {
logout: (state) => {
state.user = null;
state.token = null;
state.status = "idle";
},
},
extraReducers: (builder) => {
builder
.addCase(loginUser.pending, (state) => {
state.status = "loading";
state.error = null;
})
.addCase(loginUser.fulfilled, (state, action) => {
state.status = "succeeded";
state.user = action.payload.user;
state.token = action.payload.token;
})
.addCase(loginUser.rejected, (state, action) => {
state.status = "failed";
state.error = action.payload as string;
});
},
});
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 } as CounterState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
// ============================================
// Store Configuration
// ============================================
const rootReducer = combineReducers({
auth: authSlice.reducer,
counter: counterSlice.reducer,
});
export const store = configureStore({
reducer: rootReducer,
devTools: process.env.NODE_ENV !== "production",
});
// ============================================
// TypeScript Helpers
// ============================================
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// Use these typed hooks throughout the app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// ============================================
// Exports
// ============================================
export const { logout } = authSlice.actions;
export const { increment, decrement, incrementByAmount } = counterSlice.actions;