npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

async-data-hooks

v1.2.1

Published

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

Downloads

32

Readme

⚓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

Typedefs

useRequest(config) ⇒ UseRequestReturnedObject

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 |