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

@lbfalvy/react-await

v4.0.3

Published

A higher-order component to await all props before rendering your component

Downloads

43

Readme

React-await

Flexible streamlined data and code fetching in React

What it does

React-await allows you to resolve promises and load data as needed before rendering a component.

Why not Suspense?

Suspense is good in the narrow set of cases where it's usable, but it requires special effort from the library creator and dictates a particular data fetching strategy. This component is far more general than that. I've successfully used it to wait for HTTP requests, particular websocket messages, WebRTC connections and user decisions, but it's usable for code-splitting and pretty much any use case when you want to render something from promises or obtainers.

Additionally, Await also solves the waterfall issue with SSR simply by motivating you to fetch data and components together. While this approach is slower than Suspense with Relay, it's a lot more modular and flexible.

How to use it

Pass it whatevver component you want to render once the data is ready in the Comp prop. Add any other props directly to the component. If Comp takes a parameter called foo, the following variants are accepted:

  • The value itself under its own name
  • A promise under the value's name
  • A function that returns either the value or a promise to it, under the name f$foo. The function will be re-run whenever it changes.
  • An array containing a function on the first position followed by its dependencies, under the name f$foo. The function will be re-run whenever the parameter list changes.

The above rules can also be applied to the Comp prop. In addition, if Comp turns out to be an object with the property default, that property is used instead. This serves to make code-splitting with ES modules easier.

All of these comparisons and checks, including the dependency list, are implemented independently of React's builtins, so React constraints don't apply. You may provide a prop in different formats at different renders or change the length of the dependency list.

Custom loader and error, object children

You can specify a custom loader and error by passing an object to children and using the properties loader and error. If you also want to pass children to the wrapped component, you can use children.with. If you set children to an object that doesn't have the property with, the wrapped component will receive the whole object. You can cover every edge case by setting with to exactly what you want the component to receive, but most of the time the default should just work.

Examples

<Await Comp={MyComponent} one={1} f$two={[() => api.getTwo(), api]} f$three={globalFuncGet3} />
<Await f$Comp={[() => import('./MyComponent.mjs')]} />
<Await f$Comp={[() => customFetchComponent('MyComponent')]} />
<Await Comp={MyComponent}>some children</Await>
<Await Comp={MyComponent}>{{
  with: <>some children</>
  loader: <>Loading some parent...</>
  error: () => <FancyErrorMessage />
}}</Await>
<Await Comp={Chat} f$children={[() => fetchChildren()]}>{{
  loader: <Spinner />
  error: () => <Popup text='Failed to load messages' />
}}</Await>

Reload

An ideal usage of this component is completely declarative, the values returned by obtainers only change when their dependencies do, and so the component always knows when to re-run them. In reality though, these functions are likely using the network or another system with "pull" semantics because they're async, and so they might need to be re-run on a user interaction or timer, ideally without introducing bogus state such as a "last refresh time" that does nothing besides triggering a single update.

If you want to forget all obtainers and re-fetch your data, you can either call the reload function prop within the rendered component or the reload function patched onto the ref. This function never overrides any existing props or ref properties. Refs are forwarded in such a way that instanceof checks continue to work and hasOwnProperty or in works if and only if the ref object has no prototype.

Tricks

You can pass an inline render function to put the data consumer right next to data fetching. By setting f$Comp to a unary tuple you indicate that the component will never change (empty dependency list), so you can pass a lambda function without losing component state. You still shouldn't use Comp's dependency list to take lexical bindings from the enclosing component because every time Comp is reloaded you'd lose component state.

<Await
  f$user={[() => fetchUser(userId), userId]}
  f$Comp={[({ user }) => <>
    <h1>{user.name}</h1>
  </>]}
/>

A slightly better solution might be to pass a special component to Comp which forwards all its props to a render callback.

<Await
  f$Comp={[(props) => props.render(props)]}
  f$user={[() => fetchUser(userId), userId]}
  f$render={[() => ({ user }) => {
    const chat = useConversation(myId, user.name)
    return <SomeChatView log={chat} />
  }, myId]}
/>

Future of the project

The code is strictly separated to framework-agnostic business logic and React glue. This is mostly for maintainability, but I plan on porting it to other frameworks later and appreciate help in this regard.