question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

A simpler way to work with Loadables?

See original GitHub issue

Right now working with Loadable values is pretty tedious. It would be really nice if the API could be simplified for the 90% of common cases where you just want to know if something is loading, errored, or has data.

This is a common pattern that has been solved in nicer ways in existing libraries. For example in SWR with useSWR:

function Profile() {
  const { data, error } = useSWR('/api/user', fetcher)
  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}

Or in Apollo with useQuery:

function Hello() {
  const { loading, error, data } = useQuery(GET_GREETING, {
    variables: { language: 'english' },
  });
  if (loading) return <p>Loading ...</p>;
  return <h1>Hello {data.greeting.message}!</h1>;
}

Or slightly more verbosely in React Query with useQuery:

function Todos() {
   const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList)
 
   if (isLoading) {
     return <span>Loading...</span>
   }
 
   if (isError) {
     return <span>Error: {error.message}</span>
   }
 
   // We can assume by this point that `isSuccess === true`
   return (
     <ul>
       {data.map(todo => (
         <li key={todo.id}>{todo.title}</li>
       ))}
     </ul>
   )
 }

For 90% of cases where the data will be either T or undefined when still loading, I think SWR’s minimal API is the nicest. Either you have an error, or you have data or you’re still loading. (And for the edge cases where data can resolve successfully to undefined you can use the deeper API.)


It would be really nice to do something like:

const { data, error } = useRecoilValueLoadable(...)

Instead of the current API where you have to wrangle with the awkward bits like loadable.status === 'hasValue' or loadable.errorOrThrow().

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:1
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

7reactions
BenjaBobscommented, Feb 5, 2021

You could also create your own hook to do exactly that:

export default function useLoadable(state: RecoilState) {
    const loadable = useRecoilLoadable(state);

    return {
        loading: loadable.state === 'loading',
        error: loadable.state === 'hasError' ? loadable.contents : undefined,
        data: loadable.state === 'hasValue' ? loadable.contents : undefined,
    };
}

Apologies for typos and misremembering the syntax, I am writing this on my phone.

3reactions
ianstormtaylorcommented, Feb 10, 2021

@drarmstr sorry, I should have explained further. I’m not just talking about terseness here, but about making the syntax easier for people to understand. I think the current API has a more convoluted mental model, which makes it harder to understand/read, and results in buggier code. It causes a few different issues…

  • It’s harder to ensure you’ve handled all the states. You have to make sure you’ve written an if (or ternary) for each of the state values. Or you have to use a switch which are not easy to use inline in JSX, especially in nested children. This is more error prone than ensuring you’re using both data and error (especially since ESLint will warn for unused variables).

  • It’s harder to quickly check loading/error states. You have to check loadable.state === 'hasError', and then you have to read loadable.contents (and mentally remember that that property changes depending on the current state). This is more convoluted than just testing error and using it.

  • Harder to rename properties when using multiple loadables. You have to choose to either prefix like userLoadable and then have even longer variable usage everywhere. Or you have to prefix the awkward sub-properties like userState and userContents which are not obvious concepts. This is much more convoluted than prefixing as user and userError.

I’d encourage you to use GitHub’s code search to see how people are using the API. I think it reveals a lot of the extra confusion the mental model of state, contents, valueMaybe, errorOrThrow, ... causes, instead of the simpler data, error. There are a lot of things people are doing weirdly…

  • Forgetting to handle one of loading/error states. (1, 2, 3, 4, 5)
  • Falling into awkward switch statements which JSX doesn’t handle. (1, 2, 3, 4, 5, 6, 7)
  • Writing their own wrapper hooks to try to simplify the API (often incorrectly). (1, 2, 3)
  • Other strange logic to get around confusion with API. (1, 2, 3, 4, 5, 6, 7)

This could be solved with a single change—exposing error and data properties, instead of the current merged contents property. I don’t think so many other libraries have ended up with this API by accident, but because it’s much easier for people to understand. (Check out the other libraries’s GitHub code search results too, often much clearer.)

And it has some nice safeguarding properties too:

const { data, error } = useRecoilLoadableValue(...)

// If you forget this error check, you'll get an ESLint error for 
// the unused `error` variable, saving you.
if (error) return <Error />

// If you forget this loading check, you'll get a TypeScript error
// for accessing data when it could be undefined, saving you.
if (!data) return <Loading />

return data.map(...)

And it provides a clear set of renames when using multiple loadables at once:

const { data: user, error: userError } = useRecoilLoadableValue(...)
const { data: teams, error: teamsError } = useRecoilLoadableValue(...)

I’m not arguing for getting rid of the state, since I see the value in that discriminating ability. But I don’t think merging all the results into contents is a good approach.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Code Splitting in React Using React Loadable - DigitalOcean
Let's create a contrived example to illustrate how simple Loadable is. First, SomeComponent : components/SomeComponent.js.
Read more >
The Preferred Method to Use Loadables - Developer Help
The recommended way to use loadables is: Select the project that sets the device configuration bits and initialization as the main project (right...
Read more >
Introducing React Loadable – @thejameskyle
You also need a way to communicate in your app when something is loading. ... Instead you can use Loadable to abstract away...
Read more >
An introduction to Webpack Code-Splitting, Loadable ...
First let's ask a deceptively simple question: how big should each code bundle ... Examples here that use Loadable Components will not use...
Read more >
Code Splitting? - Loadable Components
The best way to introduce code-splitting into your app is through the dynamic import() syntax. ... Note: The dynamic import() syntax is a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found