Skip to main content

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:
KeySlicePurpose
state.userUserSliceAuthenticated user object and login flag
state.doctorsDoctorSliceDoctor 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

ReducerBehaviour
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

1

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;
  }
);
2

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

CaseState change
loadDoctors.pendingloading = true, error = null
loadDoctors.fulfilleddata = action.payload, loading = false
loadDoctors.rejectederror = action.error.message, loading = false
toggleFavorite.fulfilledFinds matching doctor in data by id, merges updated fields
toggleFavorite.rejectederror = 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.

Build docs developers (and LLMs) love