DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Brice AIELLO
Brice AIELLO

Posted on

Micro store with React Hooks and RxJS

Recently I got curious about RxJS as a replacement for Redux and all the boilerplate code needed to integrate it with a React project. For this example, I wanted to create a "micro-store" and initialise it with data from the https://randomuser.me/ API.

Creating a micro store

Let's start by creating and exporting an initial state in a store/users.js file. We will also declare a state variable that will contain the current state of this store over time.

const initialState = {
  loading: false,
  users: [],
}

let state = initialState

export default {
  initialState,
}

Then we will add a BehaviorSubject and initialise it with the initial state.

import { BehaviorSubject } from "rxjs"

let subject = new BehaviorSubject(initialState)

We can now add a function all subscribers can use to synchronise their state with the current state of our store, using their setState method.

export default {
  initialState,
  subscribe: setState => subject.subscribe(setState),
}

Lastly, we will request users array from the API and update the content of our micro-store.

import { ajax } from "rxjs/ajax"
import { catchError, delay, map } from "rxjs/operators"

subject.next({ ...state, loading: true })

ajax
  .getJSON('https://randomuser.me/api/?results=10')
  .pipe(
    delay(2000), // This simulate some latency
    map(({ results }) => subject.next({ ...state, loading: false, users: results })),
    catchError(() => subject.next({ ...state, loading: false })),
  )
  .subscribe()

The final content of store/users.js is:

import { BehaviorSubject, of } from 'rxjs'
import { ajax } from 'rxjs/ajax'
import { catchError, delay, map } from 'rxjs/operators'

const initialState = {
  loading: false,
  users: [],
}
let state = initialState

let subject = new BehaviorSubject(initialState)

subject.next({ ...state, loading: true })

ajax
  .getJSON('https://randomuser.me/api/?results=10')
  .pipe(
    delay(2000),
    map(({ results }) => subject.next({ ...state, loading: false, users: results })),
    catchError(() => subject.next({ ...state, loading: false })),
  )
  .subscribe()

export default {
  initialState,
  subscribe: setState => subject.subscribe(setState),
}

Link store to a component

import React, { useState, useEffect } from "react"
import usersStore from "./store/users"

export default function App() {
  const [{ loading, users }, setUsers] = useState(usersStore.initialState)

  useEffect(() => {
    const subscription = usersStore.subscribe(setUsers)
    return () => subscription.unsubscribe()
  }, [])

  return (
    <div className="App">
      <h1 className="text-3xl">Users:</h1>
      {loading ? (
        <p>Loading ...</p>
      ) : (
        <ul>
          {users.map(user => (
            <li key={user.email}>{user.name.first}</li>
          ))}
        </ul>
      )}
    </div>
  )
}

Result

Top comments (0)

We are hiring! Do you want to be our Senior Platform Engineer? Are you capable of chipping in across sysadmin, ops, and site reliability work, while supporting the open source stack that runs DEV and other communities?

This role might just be for you!

Apply now