class Loadable
A Loadable
object represents the current state of a Recoil atom or selector. This state may either have a value available, may be in an error state, or may still be pending asynchronous resolution. A Loadable
has the following interface:
state
: The current state of the atom or selector. Possible values are'hasValue'
,'hasError'
, or'loading'
.contents
: The value represented by thisLoadable
. If the state ishasValue
, it is the actual value, if the state ishasError
it is theError
object that was thrown, and if the state isloading
, then aPromise
of the value.
Loadables also contain helper methods for accessing the current state. Consider this API to be unstable:
getValue()
- Method to access the value that matches the semantics of React Suspense and Recoil selectors. If the state has a value then it returns a value, if it has an error then it throws that error, and if it is still pending then it suspends execution or rendering to propagate the pending state.toPromise()
: returns aPromise
that will resolve when the selector has resolved. If the selector is synchronous or has already resolved it returns aPromise
that resolves immediately.valueMaybe()
- Returns the value if available, otherwise returnsundefined
valueOrThrow()
- Returns the value if available or throws an Error.map(callback)
- Takes a function to transform the value of the Loadable and returns a newLoadable
with the transformed value. The transformation function gets a parameter of the parent Loadable's value and you can return the new value for the new Loadable; it also propagates thrown errors or suspense. Your callback function can return either a new value, aPromise
orLoadable
of a new value, or it can throw an error. This method is comparable to.then()
for Promises.
Example​
function UserInfo({userID}) {
const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
switch (userNameLoadable.state) {
case 'hasValue':
return <div>{userNameLoadable.contents}</div>;
case 'loading':
return <div>Loading...</div>;
case 'hasError':
throw userNameLoadable.contents;
}
}
Creating Loadables​
The RecoilLoadable
interface can be imported to create your own Loadable
objects.
interface RecoilLoadable {
function of<T>(T | Promise<T>, Loadable<T>): Loadable<T>;
function error<T>(mixed): Loadable<T>;
function all(Array<mixed | Loadable<mixed> | Promise<mixed>>): Loadable<Array<mixed>>;
function all({[string]: mixed | Loadable<mixed> | Promise<mixed>}): Loadable<{[string]: mixed}>;
function loading(): Loadable<empty>;
function isLoadable(mixed): boolean;
}
Examples​
RecoilLoadable.of(123);
RecoilLoadable.error(new Error('ERROR'));
RecoilLoadable.all([
RecoilLoadable.of(1),
RecoilLoadable.of(10),
RecoilLoadable.of(100),
]).map(([a, b, c]) => a+b+c);
Loadables may represent asynchronous values:
// Asynchronously resolves to 123
RecoilLoadable.of(Promise.resolve(123));
Similar to Promise.resolve()
, RecoilLoadable.of()
can accept literal values as well as Loadables or Promises, which will be unpacked:
// All resolve to 'x'
RecoilLoadable.of('x');
RecoilLoadable.of(RecoilLoadable.of('x'));
RecoilLoadable.of(Promise.resolve('x'));
Likewise, similar to Promise.all()
, RecoilLoadable.all()
can accept arrays of Loadables, Promises, or literal values:
// Resolves to [1, 2, 3]
RecoilLoadable.all([1, RecoilLoadable.of(2), Promise.resolve(3)]);
// Resolves to {value: 1, loadable: 2, promise: 3}
RecoilLoadable.all({
value: 1,
loadable: RecoilLoadable.of(2),
promise: Promise.resolve(3),
});
// Never resolves
RecoilLoadable.loading();