Refactor to Refract
Tags: react, frontend, effects, refract
Published: 2021-02-21 04:29:52

---
title: Refactor to Refract
published: true
description: My journey to transition some code from useEffects to withEffects using the library `refract`
tags: React, frontend, effects, refract
---

### Reactive Programming is hard

In 2018, I worked on a project that used RxJS to create some Observables for api calls. The code looked complicated, RxJS itself was complicated, and I had a hard time working with it.
I don't think I really understood what reactive programming was about. Overall, I disliked the concept, or to be more truthful, I found it hard to understand and that lead to frustration.

### Practice makes perfect

I started working at Fanduel about a year ago. Since then I've been delving more and more into reactive programming using the library [refract](https://github.com/fanduel-oss/refract) originally developed by folks at the company.

Refract describes itself as a way to:
> Handle your component effects and side-effects in a clear and declarative fashion by using asynchronous data streams

The library was hard to understand at first, and the benefits of it were not clear to me back then. To me, it simply seemed like an alternative to `React.useEffect`. 

Nowadays I feel more comfortable using reactive programming, streams, and `refract`. So today I decided to spend one hour refactoring some `useEffects` into a `withEffects` aperture just to test my knowledge and experience. After this session (and a year of experience), the benefits of this approach have become more clear to me. 

Below I have the original logic that used `useEffects`. For context, this logic comes from a Profile Page component that displays some user information (other users or your own) and all of the user's posts.


### Before:

```javascript
useEffect(() => {
  if (user.info && !params.userId) setUserData(user.info)
}, [user.info, params.userId])

useEffect(() => {
  if (!params.userId) return
  firebase.doUserInfoGet(params.userId).then((res) => {
    setUserData(res.data())
  })
}, [params.userId, firebase])

useEffect(() => {
  const iife = async () => {
    const postsCollection = await firebase.doUserPostsGet(
      params.userId || user.auth.uid
    )
    const newPosts = []

    postsCollection.forEach((post) => {
      newPosts.push(post.data())
    })
    setPosts(newPosts)
    setLoadingPosts(false)
  }
  iife()
}, [user.auth, params.userId, firebase])
```
###### View more on [GitHub](https://github.com/xendke/shy-app/blob/293ad5d7c67c461abbef3b59a515e4cb47cbbd85/src/views/Profile/Profile.jsx).

> The first effect decides whether or not to use your own information for the profile page according to the params coming from the url.
> The second effect makes the api call for other user's information.
> The third effect makes the api call for the user's posts.

My `useEffects` worked fine. They definitely could have used improvements on their own. But nonetheless, they were hard to understand.
My biggest concern was that when I came back to this codebase after 6 months I could not recognize:
- why the effects needed to execute &
- when they executed

Using `refract` I was able to abstract and clean up the logic that was needed by the component.

Observable streams are hard to understand at first, but once you get past that hurdle, you'll soon realize that they allow you to more easily organize effects. This is thanks to the api provided by `refract` and `xstream`. Reading the effect streams should make it easier understand why the effects are needed and when each effect should take place. 

### After:

```javascript
const aperture = (component, { firebase, user }) => {
  const userIdParam = () =>
    component.observe('match', ({ params }) => params.userId)

  const loadOtherUserInfo$ = userIdParam()
    .filter((param) => param)
    .map((userId) =>
      xs.fromPromise(
        firebase
          .doUserInfoGet(userId)
          .then((res) => res.data())
          .catch(() => ({ error: true }))
      )
    )
    .flatten()
    .compose(
      sampleCombine(component.observe('match', ({ params }) => params.userId))
    )
    .map(([userData, userId]) => ({ userData, isOwnProfile: false, userId }))

  const useOwnUserInfo$ = userIdParam()
    .filter((param) => !param)
    .mapTo({
      userData: user.info,
      isOwnProfile: true,
      userId: user.auth && user.auth.uid,
    })

  const loadPosts$ = userIdParam()
    .map((param) => param || user.auth.uid)
    .map((userId) => xs.fromPromise(firebase.doUserPostsGet(userId)))
    .flatten()
    .map((postsCollection) => {
      const posts = []
      postsCollection.forEach((post) => {
        posts.push(post.data())
      })
      return { loadingPosts: false, posts }
    })

  return xs
    .merge(useOwnUserInfo$, loadOtherUserInfo$, loadPosts$)
    .startWith({ posts: [], loadingPosts: true })
    .map(toProps)
}
```
###### View more on [GitHub](https://github.com/xendke/shy-app/blob/503d51a8373924de9b95de700c16456674045bd6/src/views/Profile/Profile.jsx).

The `refract` api definitely has a learning curve, but boilerplate aside, the effects are much more declarative and easier to come back to after a long hiatus!

TL;DR: Don't be afraid to refractor!