Search code examples
reactjsreduxaxiosredux-sagaredux-thunk

Refactoring from Redux-thunk to Redux-saga (+axios)


I ask for help with the Redux-saga, namely with refactoring the code below. Any ideas or explanations are welcome. The code below gets a list of hotels from the API on request with parameters from the function. It also has a check to see if data is currently being loaded or not at all. If the data is received, the action creator set Hotels is successfully executed. Thanks in advance for your reply.

hotels.js

export const getHotels = (cityName = 'London', date = currentDate(), days = 1, limit = 30) => {
return async (dispatch) => {
    dispatch(setIsFetching(true))
    dispatch(setIsEmpty(false))
    try {
        const response = await axios.get(`http://engine.hotellook.com/api/v2/cache.json?location=${cityName}&currency=rub&checkIn=${date}&checkOut=${addDays(date, days)}&limit=${limit}`)
        response.data.length === 0 ? dispatch(setIsEmpty(true)) : dispatch(setHotels(response.data))
    }
    catch (e) {
        dispatch(setIsEmpty(true))
    }
}

hotelsReducer.js

const SET_HOTELS = "SET_HOTELS";
const SET_IS_FETCHING = "SET_IS_FETCHING";
const SET_IS_EMPTY = "SET_IS_EMPTY";

const defaultState = {
  hotels: [],
  isFetching: true,
  isEmpty: false,
};

export const hotelsReducer = (state = defaultState, action) => {
  switch (action.type) {
    case SET_HOTELS:
      return {
        ...state,
        hotels: action.payload,
        isFetching: false,
      };
    case SET_IS_FETCHING:
      return {
        ...state,
        isFetching: action.payload,
      };
    case SET_IS_EMPTY:
      return {
        ...state,
        isEmpty: action.payload,
      };
    default:
      return state;
  }
};

export const setHotels = (results) => {return { type: SET_HOTELS, payload: results }};
export const setIsFetching = (bool) => {return { type: SET_IS_FETCHING, payload: bool }};
export const setIsEmpty = (bool) => {return { type: SET_IS_EMPTY, payload: bool }};

Solution

  • The saga is going to be very similar, you just need to replace the thunk with a new action that will trigger the saga:

    import { put, takeLatest } from "redux-saga/effects";
    
    function* fetchHotelsSaga() {
      yield put(setIsFetching(true));
      yield put(setIsEmpty(false));
      try {
        const response = yield axios.get(`http://engine.hotellook.com/api/v2/cache.json?location=${cityName}&currency=rub&checkIn=${date}&checkOut=${addDays(date, days)}&limit=${limit}`);
        response.data.length === 0
          ? yield put(setIsEmpty(true))
          : yield put(setHotels(response.data));
      } catch (e) {
        yield put(setIsEmpty(true));
      }
    }
    
    function* hotelsSaga() {
      // FETCH_HOTELS is a type for the new action that will be dispatched instead of the thunk
      yield takeLatest(FETCH_HOTELS, fetchHotelsSaga);
    }