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)