This FullCalendar application uses Redux Toolkit for centralized state management. All events and resources are stored in the Redux store, making it easy to manage, update, and share state across components.
The Redux store is configured with:
- Calendar reducer - Manages events and resources
- Middleware configuration - Handles date serialization for FullCalendar
Contains:
- Initial state with sample events and resources
- Reducers for CRUD operations
- Actions exported for use in components
- Uses
useSelectorto read state from Redux - Uses
useDispatchto dispatch actions - Handles all FullCalendar interactions
- Wraps the app with Redux
Provider - Renders the Calendar component
import { useSelector } from "react-redux";
const { resources, events } = useSelector((state) => state.calendar);import { useDispatch } from "react-redux";
import { addEvent, updateEvent } from "../store/calendarSlice";
const dispatch = useDispatch();
// Add event
dispatch(addEvent(newEvent));
// Update event
dispatch(updateEvent(updatedEvent));User drags event → handleEventDrop() → dispatch(updateEvent()) → Redux updates state → Calendar re-renders
User resizes event → handleEventResize() → dispatch(updateEvent()) → Redux updates state → Calendar re-renders
User selects time slot → handleDateSelect() → dispatch(addEvent()) → Redux updates state → Calendar re-renders
- Centralized State - Single source of truth for all calendar data
- Predictable Updates - All state changes go through reducers
- Easy Testing - Actions and reducers are pure functions
- DevTools Support - Time-travel debugging with Redux DevTools
- Scalability - Easy to add new features and state slices
- Component Decoupling - Components don't need to pass props deeply
- Open
src/store/calendarSlice.js - Add a new reducer:
reducers: {
// ... existing reducers
duplicateEvent: (state, action) => {
const eventToDuplicate = state.events.find((e) => e.id === action.payload);
if (eventToDuplicate) {
const newEvent = {
...eventToDuplicate,
id: String(Date.now()),
};
state.events.push(newEvent);
}
};
}- Export the action:
export const {
addEvent,
updateEvent,
deleteEvent,
duplicateEvent, // Add this
} = calendarSlice.actions;- Use in component:
import { duplicateEvent } from "../store/calendarSlice";
dispatch(duplicateEvent(eventId));Install the Redux DevTools Extension to:
- Inspect state changes
- Time-travel through actions
- Debug state updates
- Export/import state
- Keep reducers pure - No side effects in reducers
- Use action creators - Always use the exported actions
- Normalize state - Keep data flat when possible
- Use selectors - Create reusable selectors for complex state queries
- Handle async with thunks - Use createAsyncThunk for API calls
import { createAsyncThunk } from "@reduxjs/toolkit";
export const fetchEvents = createAsyncThunk(
"calendar/fetchEvents",
async () => {
const response = await fetch("/api/events");
return response.json();
}
);
// In slice
extraReducers: (builder) => {
builder.addCase(fetchEvents.fulfilled, (state, action) => {
state.events = action.payload;
});
};Solution: Already handled in store configuration with serializableCheck middleware settings.
Solution: Make sure you're dispatching actions, not mutating state directly.
Solution: Verify you're using useSelector correctly and the state path is correct.
Happy coding with Redux! 🚀