API Migration Guide

A comprehensive guide that covers the migration process to the new API implementation.

focusing on two main aspects:

  1. Forming the API      2.  Using the API

Forming the API

/** 
 * Get device from Glados 
 */
export function* getDeviceSaga(): Generator<StrictEffect> {
  const hardwareService = GladosServiceFactory.getHardwareService();

  // Collecting data to make the api request

  try {
    const response = (yield call([hardwareService, hardwareService.getHardwareDetailGroup], 'v1', params)) as Awaited<ReturnType<typeof hardwareService.getHardwareDetailGroup>>;
    const { data } = response;
    // Side effects handled by listeners
  } catch (error: any) {
    // Side effects handled by listeners
  }
}

/** 
 * Get subscription from Glados 
 */
export function* getSubscriptionsSaga(): Generator<StrictEffect> {
  const tariffService = GladosServiceFactory.getTariffService();

  // Collecting data to make the api request

  try {
    const response = (yield call([tariffService, tariffService.getTariffWithHardware], 'v1', params)) as Awaited<ReturnType<typeof tariffService.getTariffWithHardware<RedTariff | YoungTariff>>>;
    const { data } = response;
    // Side effects handled by listeners
  } catch (error) {
    // Side effects handled by listeners
  }
}

Using the API

In Selectors

The new API can be used with selectors to dynamically update state. Here's how to implement selectors with the API:

/**
 * Selects the HardwareDetailGroup query selector
 */
const createHardwareDetailGroupSelector = createAppSelector(
  [
    state => state.vvlDeviceDetailsApp.salesChannel,
  ],
  salesChannel => getHardwareDetailGroup.select({
    salesChannel: salesChannel!,
  }),
);

/**
 * Selects the HardwareDetailGroup query
 */
const selectHardwareDetailGroupQuery = createAppSelector(
  [
    state => state,
    createHardwareDetailGroupSelector,
  ],
  (state, selector) => selector(state),
);

/**
 * Selects the full device
 */
const selectDevice = createAppSelector(
  [
    selectHardwareDetailGroupQuery,
  ],
  query => query?.data || null,
);

In Listeners

Listeners are used to perform side effects in response to API actions:

setDefaultState: () => startAppListening({
  actionCreator: setDefaultState,
  effect: (_action, listenerApi) => {
    // rest of the side effects

    deviceId 
      ? listenerApi.dispatch(getHardwareDetailGroup.initiate({ salesChannel }))
      : redirectToDop();
  },
}),

getDeviceFulfilled: () => startAppListening({
  matcher: getHardwareDetailGroup.matchFulfilled,
  effect: (_action, listenerApi) => {
    // rest of the side effects

    listenerApi.dispatch(getTariffWithHardware.initiate({ 
      salesChannel, 
      isTradeIn 
    }));
  },
}),

In Extra Reducers

Extra reducers can be used to handle asynchronous logic without using createAsyncThunk:

extraReducers: builder => {
  builder
    .addMatcher(
      getHardwareDetailGroup.matchFulfilled,
      (state, action) => {
        const { atomics } = action.payload.data;
        
        if (
          !state.atomicId || 
          !atomics.some(atomic => atomic.hardwareId === state.atomicId)
        ) {
          const defaultAtomic = atomics.find(
            atomic => atomic.defaultAtomicDevice
          );
          state.atomicId = defaultAtomic?.hardwareId || null;
        }
        
        const currentAtomic = atomics.find(
          item => item.hardwareId === state.atomicId
        );
        state.currentColor = currentAtomic?.color || null;
        state.currentCapacity = currentAtomic?.capacity || null;
      },
    )
}