react-request-statuses

Utility library for handle request statuses

Usage no npm install needed!

<script type="module">
  import reactRequestStatuses from 'https://cdn.skypack.dev/react-request-statuses';
</script>

README

React Request Statuses

Purpose

React Request Statuses gives an ability to avoid a lot of isLoading statuses in your project and provide the simple API for subscribing on 3 statuses of your async action.

You can subscribe on the following states:

  • isLoading state
  • isSuccess state
  • Get Failed message

Code Sample

Before:

You had to create isLoading status for every action which needs handling in a template.

const initialState: UserReducer = {
  // ...
  user: {
    //...
  },
  isLoading: false,
  isSuccess: false,
  errorMessage: '',
};

export const reducer = createReducer(initialState, {
  [GET_USER_ASYNC.REQUEST.toString()]: (
    state: UserReducer,
    action: ActionType<User>,
  ) => {
    // ...
    state.isSuccess = false;
    state.isLoading = true;
    state.errorMessage = '';
  },
  [GET_USER_ASYNC.SUCCESS.toString()]: (
    state: UserReducer,
    action: ActionType<User>,
  ) => {
    // ...
    state.isSuccess = true;
    state.isLoading = false;
  },
  [GET_USER_ASYNC.FAILURE.toString()]: (
    state: UserReducer,
    action: ActionType<ApiError>,
  ) => {
    // ...
    state.isSuccess = false;
    state.isLoading = false;
    state.errorMessage = action.payload.message;
  },
});

After:

After you no longer need to create an isLoading status, since you have the separate place with registered statuses for all actions.

const initialState: UserReducer = {
  user: {
    // ...
  },
};

export const reducer = createReducer(initialState, {
  [GET_USER_ASYNC.SUCCESS.toString()]: (
    state: UserReducer,
    action: ActionType<User>,
  ) => {
    merge(state.user, action.payload);
  },
});

Components usage:

Now if you are interested in some status of your action,it is simple to subscribe on that in the template.

import React, { useEffect } from "react";
import { useIsLoadingSelector, useIsSuccessSelector, useReasonFailedSelector } from "react-request-statuses";
import { GET_USER_ASYNC } from "@/store/user/actions";

export const SomeComponent = () => {
  const isLoading = useIsLoadingSelector(GET_USER_ASYNC)
  const isSuccess = useIsSuccessSelector(GET_USER_ASYNC)
  const errorMessage = useReasonFailedSelector(GET_USER_ASYNC)

  useEffect(() => {
    if(errorMessage) {
      alert(errorMessage)
    }
  }, [errorMessage])

  useEffect(() => {
    if(isSuccess) {
      alert('Success!')
    }
  }, [isSuccess])

  if(isLoading) {
    return <FullScreenLoader />
  }

  return (
    /// ..
  )
}

Installation

Open a Terminal in the project root and run:

yarn add react-request-statuses

or

npm i react-request-statuses

Setup

  • Add requestStatuses to your root reducer.
import { requestStatuses } from 'react-request-statuses';

export const rootReducer = combineReducers({
  requestStatuses, // <- add here
  // ...
})
  • Then create async action for an action that you need to know a status using createAsyncAction from react-request-statuses;
import { createAsyncAction } from 'react-request-statuses'; // <- required

export const GET_USER_ASYNC = createAsyncAction(ActionTypes.GET_USER);
  • Then in a template using useIsLoadingSelector you can subscribe to loading state of your action.
import React, { useEffect } from "react";
import { useIsLoadingSelector, useIsSuccessSelector, useReasonFailedSelector } from "react-request-statuses";
import { GET_USER_ASYNC } from "@/store/user/actions";
import { useDispatch } from 'react-redux'

export const SomeComponent = () => {
  const dispatch = useDispatch()

  const isLoading = useIsLoadingSelector(GET_USER_ASYNC) // subscribe here

  useEffect(() => {
    dispatch(GET_USER_ASYNC.REQUEST()) // trigger request
  }, [])


  if(isLoading) {
    return <FullScreenLoader />
  }

  return (
    /// ..
  )
}
  • Update your Saga with call SUCCESS or FAILURE statuses of your action.
import { GET_USER_ASYNC } from "@/store/user/actions"
import { api } from "@api"

export function* getUserSaga(): SagaIterator {
  try {
    const userDetails = yield call(api.user.getUser);

    yield put(GET_USER_ASYNC.SUCCESS(userDetails.response)); // <- required
  } catch (error) {
    yield put(GET_USER_ASYNC.FAILURE(error)); // <- required
  }
}

API reference

useIsLoadingSelector

Allow subscribing to handle isLoading state.

export const SomeComponent = () => {
  const isLoading = useIsLoadingSelector(GET_USER_ASYNC)
  // Also you may subscribe for array of actions
  const isLoading = useIsLoadingSelector([GET_USER_ASYNC, UPDATE_USER_ASYNC])

  return (
    // ...
  )
}

useIsSuccessSelector

Return true if request has been successful

const isSuccess = useIsSuccessSelector(GET_USER_ASYNC)

useIsLoadingSelector

Return success payload

// somewhere
dispatch(GET_USER_ASYNC.SUCCESS({ id: 1, name: 'Jhon' }))
const payload = useSuccessPayloadSelector(GET_USER_ASYNC)
// { id: 1, name: 'Jhon' }

useIsFailedSelector

Return true if request has been failed

const isFailed = useIsFailedSelector(GET_USER_ASYNC)

useReasonFailedSelector

Return error message

// somewhere
dispatch(GET_USER_ASYNC.FAILURE({ code: 401, message: 'Unauthorized' }))
const errorMessage = useReasonFailedSelector(GET_USER_ASYNC)
// 'Unauthorized'

useActionSubscription

Create the subscription for an action

export const SomeComponent = () => {
  const getUserSubscription = useActionSubscription(GET_USER_ASYNC)

  const successCallback = (successPayload) => {
    //..
  }

  const failedCallback = (errorMessage) => {
    //..
  }

  getUserSubscription(successCallback, failedCallback)

  return (
    // ...
  )
}