0.0.3 • Published 5 years ago

redux-resource-factory v0.0.3

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

redux-resource-factory

CircleCI

Handle resources like a boss 😎.

API | Examples

redux-resource-factory provides useful abstraction over redux-managed resources that:

  • are (typically) asynchronous
  • succeed / fail
  • might get cancelled / reset

Usage:

import { createStore } from "redux";
import { createResource } from "redux-resource-factory";

const { reducer, request, success } = createResource<User[], string>("USERS");

const store = createStore(reducer);

type AppState = ReturnType<typeof reducer>; // Resource<User[], string>

store.getState(); // { data: null, error: null, loading: false }

store.dispatch(request());

store.getState(); // { data: null, error: null, loading: true }

store.dispatch(success({ data: [] }));

store.getState(); // { data: [], error: null, loading: false }

API

Resource<T, E>

Represents a resource of type T that can fail with an error of type E.

type Resource<T, E> = {
  data: T | null;
  error: E | null;
  loading: boolean;
};

resource(init?)

Used to create instances of Resource.

Arguments:

  • init?: Partial<Resource<T, E>> - overrides (default values used otherwise)

Returns:

Resource<T, E>

The default values are:

{
  data: null,
  error: null,
  loading: false,
}

createResource(tag, options?)

Used to create a reducer for managing the particular resource, along with corresponding action creators.

Arguments:

  • tag: string - a unique string identifier of a resource (used internally to filter corresponding actions)
  • options?: { initialState?: Resource<T, E> } - additional options (currently only include specifying initial state)

Returns:

{ reducer, ...actionCreators }

where reducer has the following signature:

(
  state: Resource<T, E> | undefined,
  action: ReturnType<typeof actionCreators[keyof typeof actionCreators]>
) => Resource<T, E>;

and the actionCreators are:

request()

{
  data: null,
  error: null,
- loading: false,
+ loading: true,
}

success(payload: { data: T })

{
- data: null,
+ data: [{...}, {...}],
  error: null,
- loading: true,
+ loading: false,
}

error(payload: { data: E })

{
  data: null,
- error: null,
+ error: "Unauthorized",
- loading: true,
+ loading: false,
}

reset()

{
- data: [{...}, {...}],
+ data: null,
  error: null,
  loading: false,
}

cancel()

{
  data: null,
  error: null,
- loading: true,
+ loading: false,
}

Examples

With redux-thunk

export const fetchUser = (
  userId: User["id"],
  cancel: Deferred<void>
): ThunkAction<Promise<void>, AppState, any, AnyAction> => async dispatch => {
  dispatch(request());

  try {
    const { data } = await axios.get(
      `/users/${userId}`,
      {
        cancelToken: new CancelToken(c => cancelDefer.promise.then(() => c())),
      }
    );

    dispatch(
      success({ data })
    );
  } catch (err) {
    dispatch(
      isCancel(err)
        ? cancel()
        : error({ data: err })
    );
  }
};

With redux-saga

function* fetchUserSaga(action: SomeAction) {
  const source = axios.CancelToken.source();

  yield put(request());

  try {
    const { data } = yield call(
      axios.get,
      `/users/${action.payload.userId}`,
      { cancelToken: source.token },
    );

    yield put(success({ data }));
  } catch (err) {
    yield put(error({ data: err }));
  } finally {
    if (yield cancelled()) {
      yield put(cancel());

      source.cancel();
    }
  }
}