DEV Community

loading...
Cover image for Simple Remote Data in Typescript

Simple Remote Data in Typescript

rametta profile image Jason ・3 min read

Introducing Simple Remote Data (SRD), a Static Land compliant TypeScript library for managing state from remote sources.

SRD's top features are:

  • Only 549 bytes minified and gzipped, that is ridiculously small!
  • Built with Higher Kinded Types (HKT's) to allow Typescript to infer these complex disjoint union types.
  • Static Land compliant!
  • Made for React or any other JS based frontend framework.

What is a Remote Data type?

A Remote Data type is a kind of discriminate union type in functional programming for managing the state of some data that is retrieved remotely and asynchronously, such as http response data, websockets, server sent events, reading a file, or any other async IO.

Remote Data greatly simplifies the 4 states of any IO operation - initial state, loading state, failure state or success state, by combining them into one pure union so none of these states can overlap and cause impossible states.

Why would I use a Remote Data?

To simplify your state model and prevent impossible states! SRD allows us to perform safe and pure computations on our data without redundant null checks.

It also allows us to cleanly represent our state in our UI applications by providing functions such as match which is our pattern matcher utility.

How would I use a Remote Data?

Here is a popular use case in any React application - fetching data from somewhere and displaying it on screen, while also displaying loading states and error states if the fetch fails.

import React, { useState, useEffect } from 'react'
import { SRD, notAsked, loading, failure, success } from 'srd'

const App = () => {
  const [rd, setRd] = useState(notAsked())

  useEffect(() => {
    setRd(loading())
    fetch('...')
      .then((data) => setRd(success(data)))
      .catch((err) => setRd(failure(err)))
  }, [])

  return SRD.match({
    notAsked: () => <div>Empty</div>,
    loading: () => <div>Loading...</div>,
    failure: (err) => <div>{err}</div>,
    success: (data) => <div>{data}</div>,
  }, rd)
}
Enter fullscreen mode Exit fullscreen mode

Without SRD, we would need to manually manage states for errors, loading and initial states separately - with SRD the code is a lot smaller, easier to follow and most importantly, safer.

What else?

SRD also provides a lot of great functions for accessing and modifying the data inside the Remote Data.

If we needed to modify the data inside a success we can use the popular map function to do so, it will allow us to run a function on the inside data without worrying if it's a success or not.

Here is a TypeScript React example for updating an object:

const [rdPerson, setPerson] = useState<RD<string, Person>>(notAsked())

useEffect(() => {
  setPerson(loading())
  fetch('...')
    .then((person) => setPerson(success(person)))
    .catch((err) => setPerson(failure(err.msg)))
}, [])

const doubleAge = (person: Person): Person =>
  ({ ...person, age: person.age * 2 })

const handleClick = () => {
  setPerson(rd => SRD.map(doubleAge, rd))
}

return <>
  {
    SRD.match({
      notAsked: () => 'Empty',
      loading: () => 'Please wait...',
      failure: (msg) => `There was a problem: ${msg}`,
      success: person => `My age is ${person.age}`
    }, rdPerson)
  }
  <button onClick={handleClick}>
    click to double my age
  </button>
</>
Enter fullscreen mode Exit fullscreen mode

And map is not the only thing we can do, SRD provides over a dozen more utilities for managing your SRD state!

I hope this article clarified the advantages of using a Remote Data type and how to use SRD, I encourage you to try it out! If you do, feel free to "Star" 🌟 the repo on github!

Discussion

pic
Editor guide