Documentation Index
Fetch the complete documentation index at: https://mintlify.com/BhushanBadhe39/SkinFirts/llms.txt
Use this file to discover all available pages before exploring further.
SkinFirts manages global application state with Redux Toolkit. The store is composed of two slices — user and doctors — each responsible for a clearly bounded domain. UserSlice tracks the currently authenticated user and their login status, while DoctorSlice handles all asynchronous doctor data operations including fetching the full catalogue and toggling favorites. Redux Toolkit’s createSlice and createAsyncThunk utilities keep the boilerplate minimal and the async lifecycle states (loading, error) predictable.
Store Configuration
The store is created once in src/redux/Store.js and exported as a named store constant that is passed to the <Provider> in App.jsx.
// src/redux/Store.js
import { configureStore } from "@reduxjs/toolkit";
import userReducer from './Slices/UserSlice';
import doctorsReducer from './Slices/DoctorSlice';
export const store = configureStore({
reducer: {
user: userReducer,
doctors: doctorsReducer,
},
});
The two top-level state keys this produces are:
| Key | Slice | Purpose |
|---|
state.user | UserSlice | Authenticated user object and login flag |
state.doctors | DoctorSlice | Doctor catalogue, loading state, and error |
UserSlice
UserSlice owns the authentication state for the app. It exposes two synchronous reducers — setUser and clearUser — that are dispatched after a successful login/register or on logout respectively.
Initial State
const initialState = {
user: null,
isLoggedIn: false,
};
Reducers
| Reducer | Behaviour |
|---|
setUser(state, action) | Sets state.user to action.payload and flips state.isLoggedIn to true |
clearUser(state) | Resets state.user to null and state.isLoggedIn to false |
Full Source
// src/redux/Slices/UserSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
user: null,
isLoggedIn: false,
};
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
setUser(state, action) {
state.user = action.payload;
state.isLoggedIn = true;
},
clearUser(state) {
state.user = null;
state.isLoggedIn = false;
},
},
});
export const { setUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
DoctorSlice
DoctorSlice manages the full list of doctors retrieved from the API and handles the favorite-toggle action optimistically via a PATCH call. Both operations are modelled as createAsyncThunk actions so their pending, fulfilled, and rejected lifecycle states are handled automatically in extraReducers.
State Shape
{
data: [], // Array of doctor objects returned from the API
loading: false, // True while loadDoctors is in-flight
error: null, // Error message string if the last request failed
}
Async Thunks
loadDoctors
Calls fetchDoctors() from the API layer (a GET to ./doctorData) and returns the result array. The slice populates state.data on fulfillment.export const loadDoctors = createAsyncThunk(
'doctors/load',
async () => {
const data = await fetchDoctors();
return data;
}
);
toggleFavorite
Accepts a full doctor object, calls updateDoctors(doctor.id, { isFavorite: !doctor.isFavorite }) (a PATCH), and returns the updated doctor. On fulfillment the slice finds the matching entry in state.data by id and merges the update in-place.export const toggleFavorite = createAsyncThunk(
'doctors/toggleFavorite',
async (doctor) => {
const updated = await updateDoctors(doctor.id, { isFavorite: !doctor.isFavorite });
return updated;
}
);
Lifecycle State Handling
| Case | State change |
|---|
loadDoctors.pending | loading = true, error = null |
loadDoctors.fulfilled | data = action.payload, loading = false |
loadDoctors.rejected | error = action.error.message, loading = false |
toggleFavorite.fulfilled | Finds matching doctor in data by id, merges updated fields |
toggleFavorite.rejected | error = action.error.message |
Full Source
// src/redux/Slices/DoctorSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { fetchDoctors, updateDoctors } from '../../api/doctorsApi';
export const loadDoctors = createAsyncThunk(
'doctors/load',
async () => {
const data = await fetchDoctors();
return data;
}
);
export const toggleFavorite = createAsyncThunk(
'doctors/toggleFavorite',
async (doctor) => {
const updated = await updateDoctors(doctor.id, { isFavorite: !doctor.isFavorite });
return updated;
}
);
const doctorsSlice = createSlice({
name: 'doctors',
initialState: {
data: [],
loading: false,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadDoctors.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(loadDoctors.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
})
.addCase(loadDoctors.rejected, (state, action) => {
state.error = action.error.message;
state.loading = false;
})
.addCase(toggleFavorite.fulfilled, (state, action) => {
const updated = action.payload;
const index = state.data.findIndex(d => d.id === updated.id);
if (index !== -1) {
state.data[index] = { ...state.data[index], ...updated };
}
})
.addCase(toggleFavorite.rejected, (state, action) => {
state.error = action.error.message;
});
},
});
export default doctorsSlice.reducer;
Accessing State in Components
Use the typed useSelector and useDispatch hooks from react-redux to read state and dispatch actions from any function component.
import { useSelector, useDispatch } from 'react-redux';
import { loadDoctors, toggleFavorite } from '../redux/Slices/DoctorSlice';
import { setUser, clearUser } from '../redux/Slices/UserSlice';
// Inside a component:
const { data: doctors, loading, error } = useSelector(state => state.doctors);
const user = useSelector(state => state.user.user);
const isLoggedIn = useSelector(state => state.user.isLoggedIn);
const dispatch = useDispatch();
// Fetch all doctors
dispatch(loadDoctors());
// Toggle a specific doctor's favourite status
dispatch(toggleFavorite(doctor));
// Set user after login
dispatch(setUser(userObject));
// Clear user on logout
dispatch(clearUser());
Provider Wiring in App.jsx
The <Provider> component from react-redux wraps the entire component tree at the root of App.jsx, making the store accessible to every component via useSelector and useDispatch:
// App.jsx
import { Provider } from 'react-redux';
import { store } from './src/redux/Store';
import { ContextProvider } from './src/constants/ContextProvider';
function App() {
return (
<Provider store={store}>
<ContextProvider>
<SafeAreaProvider>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<StackNavigator />
</SafeAreaProvider>
</ContextProvider>
</Provider>
);
}
ContextProvider sits just inside <Provider> in the app tree. It supplies a DisabledDatesContext used by the scheduling screens to track which dates and time slots are already booked. This context is separate from Redux and holds only ephemeral scheduling UI state — see src/constants/ContextProvider.js for the full implementation.