Scratching the surface of composition with React Native and Apollo
Tags: reactnative, functional, graphql, javascript
Published: 2020-01-02 15:27:34

When I first stumbled upon Andrew Clark's [`recompose`](https://github.com/acdlite/recompose) library I thought awesome, I'm always up for some composition! However, a quick glance at the docs left me feeling like there was a large learning curve ahead of me as I was still just getting comfortable with React Native and GraphQL.

In this post I'll share a few recipes that helped me get started with `recompose` that had a high degree of impact of the quality of my code. The code examples below are from a project I've been working on called [Broce](https://broce.com/). 
At a high level, the tech stack is:
* React Native
* Expo
* React Apollo
* GraphQL backend in Ruby/Rails

## On the Menu today
* **Starter:** Factor out reusable logic with pure, composable functions
* **Main Course:** Factor out fetching remote data from our component all together
* **Dessert:** Convert our component to a React PureComponent

## Tasting Notes
* This article assumes you have experience with React and GraphQL
* Are familiar with or have dappled in composition and higher-order functions  

## Let's eat!

If you follow the React and Apollo docs you'll quickly end up with a component that looks like the following:

```js
const COMPANY_QUERY = gql`{
  company {
  	name
  	website
  }
}`;

export default class CompanyScreen extends React.Component {
  render() {
    return (
      <Query query=>
        {({ client, loading, error, data }) => {
          if (loading) return <LoadingMask/>;
          if (error) return <ErrorScreen error=/>;

          return (
            <ScrollView>
              <CompanyForm company=/>
            </ScrollView>
          );
        }}
      </Query>
    );
  }
}
```

This component has a few responsibilities:
1. It extends a `React.Component` and is responsible for rendering the component's layout
1. The `CompanyScreen`'s render element is wrapped by Apollo's `Query` component so that it can fetch data from the GraphQL server
1. It handles the loading and error states for the respective GraphQL query

It's fair to say that Uncle Bob would have an opinion about such a component. We're violating the Single Responsibility Principle a few times over. My main issue with Apollo's `Query` wrapping component is that it couples the concern of fetching remote data with display logic.

## Appetizer

Our first step is to factor away those 2 `if` conditions that deal with loading and error states. I had been copy and pasting that code around and could easily imagine scenarios where that logic would get more complex (think different error types that warrant different handlers).

We can create 2 plain old javascript constants which leverage recompose's `branch` function:

```js
export const displayLoadingState = branch(
  (props) => props.data.loading,
  renderComponent(LoadingMask)
);

export const displayErrorState = branch(
  (props) => props.data.error,
  renderComponent(ErrorScreen)
);
```

The `branch` function takes 3 arguments. The first is a `test` function, the second and third arguments are the potential return components if the `test` functions returns either true or false. Really, it's just another way to go about an if/else condition.

Our test functions look at the component's Apollo provided props and checks if the `data.loading` or `data.error` states are set. In that event that the query is loading or returned an error, we call recompose's 
`renderComponent` function, passing it our beautifully styled LoadingMask and ErrorScreen components. In the falsey case, we do nothing as we want our CompanyScreen component to render.

*A litter further down we'll see how recompose manages to pass the component's props to the `test` functions above, for now let's just assume magic is real and the props will safely arrive*   

## Main course

Now, let's go about removing that Apollo query logic from our `CompanyScreen` component.

The `react-apollo` library offers a HOC function called `graphql` which will allow us to avoid wrapping our screen components with `<Query />`. A Higher-Order-Component (HOC) is just a function that takes a component as an argument and returns a new component. All `recompose` functions are just that, HOC component functions. We'll chain them together shortly.

Introducing Apollo's `graphql` HOC function will replace `<Query query=> ...` with `graphql(COMPANY_QUERY)`. This will be the first function passed to our composable component chain. Apollo will take and execute that query, returning a new component whose props receive Apollo's `data` object.

We've managed to factor away a lot of functionality but need to stitch it all back up. 

```js
class CompanyScreen extends React.Component<Props> {
  render() {
    const = this.props;
    
    return (
      <ScrollView>
        <CompanyForm company=/>
      </ScrollView>
    );
  }
}

export default compose(
  graphql(COMPANY_QUERY),
  displayLoadingState,
  displayErrorState,
)(CompanyScreen);
```

We can see a lot of code is gone from the `CompanyScreen` component's render function. At the same time, we've introduced a new default export to this file. We no longer export the `CompanyScreen` class itself, but rather we export the component that recompose's `compose` function will create for us.

The call to `compose` at the bottom of the file will take multiple higher-order components and create a single HOC. This means our resulting `CompanyScreen` component will have triggered our GraphQL query and Apollo will handle putting the ever important `data` object onto its props. `recompose` will also handle chaining the component's props as arguments to each one of the HOC functions passed to `compose`.

Our CompanyScreen component now only has one concern, rendering a layout in the case of company data having been fetched. Uncle Bob would be proud.

## Dessert 

For desert we're going to convert our React component into a pure component, as it does not maintain any state. Only the declaration of the `CompanyScreen` needs to change here. Rather than declaring it as a class we declare it as a function, one that receives and de-structures the props argument. 

```js
const CompanyScreen = (     ) => {
  return (
    <ScrollView>
      <CompanyForm company=/>
    </ScrollView>
  );
};

export default compose(
  graphql(COMPANY_QUERY),
  displayLoadingState,
  displayErrorState,
)(CompanyScreen);
```