Skip to content

⚓A set of React hooks for data fetching and posting with precise state control.

Notifications You must be signed in to change notification settings

LookRain/async-data-hooks

Repository files navigation

⚓Async Data Hooks

A set of React custom hooks for data requesting and posting with xstate integrated, providing precise state transitions without the risk of yielding impossible states.

Installation

yarn

yarn add async-data-hooks

or

npm

npm install --save async-data-hooks

Example usage

useRequest

import { useRequest } from 'async-data-hooks';

export const getDog = async (params?: { [x: string]: string }) => {
  const res = await getJSON<DogResponseData>(`/api/puppies?${convertToString(params)}`));
  return res.data;
};

const DogComponent = () => {
  const { matcher, data, load } = useRequest<DogResponseData>({
    name: 'cute puppies', // Optional
    requestFn: getDog, // Function that takes in offset, limit and additional params, returns a promise
  });

  return (
    <div>
      <button onClick={() => load({ color: 'brown' })}>
        Load brown dog
      </button>

      <button onClick={() => load()}>
        Load default dog
      </button>

      {matcher.requesting && <p>Loading...</p>}

      {matcher.success && <div>{JSON.stringify(data)}}
    </div>
  );
};

If your data is paginated, you need to handle the offset in your own component either in a state or url search param. The hook and state machine knows nothing about your current offset. Your requestFn need to take into consideration the offset and request the correct page.

POST requests

import { useRequest } from 'async-data-hooks';

const uploadCats = async (cat: Cat) => await postJSON('/api/cats', cat);

const CatsComponent = () => {
  const { matcher, data, post } = useRequest<Cat, CatResponseData>({
    name: 'upload cats', // Optional
    requestFn: uploadCats, // Function that takes the data to post, returns a promise
  });

  return (
    <div>
      <button
        onClick={() => {
          post({
            name: 'nyan cat',
            color: 'pink grey',
            description: 'omagad this cats pukes rainbows!!!',
          });
        }}
      >
        upload nyan cat
      </button>

      {matcher.requesting && <p>Uploading...</p>}

      {matcher.success && (
        <div>
          <h1>Upload success, returned data:</h1>
          {JSON.stringify(data)}
        </div>
      )}
    </div>
  );
};

State Machine

Under the hood, a finite state machine determines what state data is in. You can access the state directly from xstateNode.value, and compare it manually, or just use the matchState function exposed, or use the very handy matcher object that contains booleans like requesting success fail for easy state matching. With a state machine, all possible state transitions are pre-defined. The benefits of such approach is:

  1. Impossible states are avoided automatically. For example:

    • Problem can occur when your data is loaded but the error object and isError flag are left unchanged from the last failed call. State machine only executes your side effect at the correct state and makes sure error is already cleared before it's executed, so such problem will not occur.
    • When you already fired a request but the user triggers another request before the previous one is resolved. You can try to avoid this by disabling the button, or adding a isLoading flag check before you execute the call, but this requires you to manage multiple flags as states, resetting them at the correct time and check them manually when needed. State machine elegantly solves this problem, it does not allow a REQUEST event on FETCH_PENDING state, so such request will not fire at all.
  2. Simplified way to check data state:

    • you no longer need to manage multiple flags and checks like !isLoading && !error && data !== null && <div>{data}</div>. all you need is matcher.success && <div>{data}</div>.

requestDataMachine

image

https://xstate.js.org/viz/?gist=e7160418a7b8ef1562659a710bdf1153

As the diagram illustrates:

  • the machine starts from the Idle state, on REQUEST event it will transition to RequestPending state invoking requestData service at the same time.
  • if requestData resolves, it will send done event transitioning the machine to LoadFinished.LoadSucceeded and executes the action updateData to update the context
  • if requestData fails, it will send error event transitioning the machine to LoadFinished.LoadFailed and executes the action updateError to update the context
  • LoadFinished state (containing 2 sub states) allows another REQUEST to be sent, also executes clearError if it's in LoadFailed state

API

Functions

useRequest(config)UseRequestReturnedObject

Create hook to perform a blocking request request for data (You can't request data when a request has not been resolved or rejected).

Typedefs

UseRequestReturnedObject : Object

useRequest(config) ⇒ UseRequestReturnedObject

Create hook to perform a blocking request request for data (You can't request data when a request has not been resolved or rejected).

Kind: global function
Returns: UseRequestReturnedObject -

returnedObject

Param Type Description
config useRequestHookConfig

Config file with compulsory request function requestFn, optional name for debugging and

UseRequestReturnedObject : Object

Kind: global typedef
Properties

Name Type Description
data any

xstateNode.context.data exposed for easy access to the data in the node

error any

xstateNode.context.error exposed for easy access to the error in the node

matcher object

object containing boolean values requesting, finished, success, fail for easy state maching

load function

exposed function for loading data

matchState function

exposed xstateNode.matches function for matching states, use StateTypes for comparison

xstateNode StateNode

as the name implies, the representation of the current xstate state https://xstate.js.org/docs/guides/states.html#state-definition. You shouldn't need access to this object but it is still exposed in case you need advanced functionality with xstate

About

⚓A set of React hooks for data fetching and posting with precise state control.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published