A simpler way to work with Loadables?
See original GitHub issueRight 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:
- Created 3 years ago
- Reactions:1
- Comments:12 (5 by maintainers)
Top GitHub Comments
You could also create your own hook to do exactly that:
Apologies for typos and misremembering the syntax, I am writing this on my phone.
@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 thestate
values. Or you have to use aswitch
which are not easy to use inline in JSX, especially in nested children. This is more error prone than ensuring you’re using bothdata
anderror
(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 readloadable.contents
(and mentally remember that that property changes depending on the current state). This is more convoluted than just testingerror
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 likeuserState
anduserContents
which are not obvious concepts. This is much more convoluted than prefixing asuser
anduserError
.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 simplerdata, error
. There are a lot of things people are doing weirdly…switch
statements which JSX doesn’t handle. (1, 2, 3, 4, 5, 6, 7)This could be solved with a single change—exposing
error
anddata
properties, instead of the current mergedcontents
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:
And it provides a clear set of renames when using multiple loadables at once:
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 intocontents
is a good approach.