RemoteData — loading states
Every async data fetch has exactly four moments: before it starts, while it’s loading, when it fails, and when it succeeds. That’s the whole picture. But it’s common to spread these four states across separate variables that can get out of sync and produce combinations that shouldn’t exist. RemoteData<E, A> gives each state a name and keeps them mutually exclusive — only one is active at any given time.
The problem with flag soup
Section titled “The problem with flag soup”A typical approach to loading states looks like this:
Three separate pieces of state, but they’re not actually independent — only certain combinations are meaningful. The type allows loading: true and error: "timeout" at the same time, which is contradictory. Nothing prevents you from forgetting to reset error when a new request starts, or showing stale data while loading is true.
The RemoteData approach
Section titled “The RemoteData approach”RemoteData makes the states explicit and mutually exclusive. There’s one value with one state at a time:
Each state is represented once, and they can’t overlap. The type system prevents you from combining them incorrectly.
Creating RemoteData values
Section titled “Creating RemoteData values”Rendering all four states with match
Section titled “Rendering all four states with match”match is the primary way to consume a RemoteData. It requires a handler for every state, so the compiler ensures you’ve covered all cases:
Because all four branches are required, there’s no way to accidentally skip the loading state or forget to handle errors. The type checker will tell you if a case is missing.
fold does the same thing with positional arguments:
Transforming the success value with map
Section titled “Transforming the success value with map”map transforms the value inside Success, leaving all other states unchanged:
This lets you transform data as part of a pipeline without breaking out of the RemoteData context:
Transforming errors with mapError
Section titled “Transforming errors with mapError”mapError transforms the error inside Failure, leaving other states unchanged:
Useful for normalizing error types from different sources before they reach your rendering logic.
Chaining dependent fetches
Section titled “Chaining dependent fetches”chain sequences a second fetch that depends on the result of the first. If the current state is Success, it passes the value to the function and returns whatever that produces. All other states pass through:
If userData is Loading, Failure, or NotAsked, the chain step is skipped and that state propagates.
Recovering from failures
Section titled “Recovering from failures”recover provides a fallback RemoteData when the current state is Failure. Unlike Result.recover, it receives the error value so you can use it in the recovery logic:
Extracting the value
Section titled “Extracting the value”getOrElse — returns the success value or a default for any other state:
Converting to other types
Section titled “Converting to other types”When you need to work with a part of the system that uses Option or Result, you can convert:
toOption — Success becomes Some, everything else becomes None:
toResult — Success becomes Ok, Failure becomes Err. NotAsked and Loading become Err using a fallback error you provide:
When to use RemoteData
Section titled “When to use RemoteData”Use RemoteData when:
- You’re displaying fetched data in a UI and need to handle all loading states explicitly
- You want the type system to prevent invalid state combinations like simultaneous loading and error
- You want a single value in state instead of three separate flags
It’s also useful outside UI contexts — any time you’re tracking the lifecycle of an async operation and need to distinguish “hasn’t started” from “in progress” from “done”.