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

@dominicprice/remotedata

v1.0.0

Published

Remote Data library for typescript

Downloads

4

Readme

@dominicprice/remotedata: Yet Another Remote Data Library

Just another remote data library which does things how I like them.

Installation

Available on npm:

npm install @dominicprice/yard

Basic Usage

Basic usage revolves around the RemoteData interface and the promise and fold functions. Suppose you have some React component which fetches some data from an API, e.g.

const MyComponent = () => {
  const [users, setUsers] = useState<User[]>([])
  useEffect(() => {
   client.getUsers().then(resp => setUsers(resp)); 
  }, [])
  
  return (
    <table>
        {users.map(user => <tr key={user.id}><td>{user.name}</td><td>{user.last_login}</td></tr>)}
    </table>
  );
}

This is fine, but there are some problems:

  • There is no feedback to the user while the data is being fetched
  • The page just appears to be blank if there is an error in the request

We can solve all of this by adding a flag and error (e.g. userFetchStatus userFetchError) to our state, but there are also problems with doing that:

  • We have to remember to check the flag each time (no IDE support)
  • If we store null as a default value, we have to type assert based on the flag's value

RemoteData handles storing a flag and error value in a type-safe way:

import * as rd from "@dominicprice/remotedata";

const MyComponent = () => {
  const [users, setUsers] = useState<rd.RemoteData<User>>(null);
  useEffect(() => {
    rd.promise(client.getUsers(), setUsers);
  })
  
  return (
    <table>
      {rd.fold(users, {
        success: (data) => data.map(user => <tr key={user.id}><td>{user.name}</td><td>{user.last_login}</td></tr>),
        loading: () => <tr><td colSpan={2}>Loading...</td></tr>,
        error: (err) => <tr><td colSpan={2}>Error fetching users: {err}</td></tr>,
        notAsked: () => null,
      })}
    </table>
  );
}

The RemoteData type can be in four different states:

  • notAsked: before any fetching has taken place, represented by a null value.
  • loading: while a promise is being resolved, has no extra data
  • success: after a promise is successfully resolved, contains an object of the specified type
  • error: after a promise rejects, contains an Error object

The promise function accepts a promise and a callback to assign the result of the promise to a RemoteData object. In React, this can just be a state setter.

The fold function accepts a remote data object and an object defining what to do in each of the four possible cases. All four cases must be defined, or if you only care about a subset of the four cases you can use a default case to define fallback behaviour:

rd.fold(users, {
  success: data => <div>There are {data.length} users</div>
  default: () => <div>There are no users</div>,
})

The promise function also accepts an additional third argument to define callbacks when the remote data reaches a different stage:

  • onError: (err: Error) => void: when the promise resolves to an error
  • onSuccess: (value: T) => void: when the promise resolves successfully
  • onLoading: () => void: when the promise enters the loading state

For example, to show a toast when an error occurs:

rd.promise(client.getUsers(), setUsers, {
  onError: (err) => displayErrorToast(err),
})

Additional uses

There are some other convenience functions provided:

must

When you only care about the success state, you can use the must function which returns the successful value if the remote data is in the success state, or undefined otherwise. For example, if you have a component which some other logic means is only displayed when the remote data is in the success state, you can use

<UserAvatar user={rd.must(user)!} />

derive

If you have a component which only depends on a particular field of the fetched data, you can instantiate a RemoteData object for that field using the derive function:

const NameBadge = ({name}: {name: rd.RemoteData<string>}) => {
  return <div>{rd.fold(name, {
    success: name => name,
    loading: () => "Loading",
    error: () => "Unknown",
    notAsked: () => "",
  })}</div>;
}

const MyComponent = ({userId}: {userId: string}) => {
  const [user, setUser] = useState<rd.RemoteData<User>>(null);
  
  useEffect(() => {
    rd.promise(client.getUser(userId), setUsers);
  }, [userId]);
  
  return (
    <div>
      <h1>Welcome to your page</h1>
      <NameBadge name={rd.derive(user, u => u.name)} />
    </div>
  );
}

This can also be used with the always and never constructors, which create a RemoteData object in the success and error states respectively.

<NameBadge name={rd.always("Admin")} />