1

Why does Artsy use Relay?

 3 years ago
source link: https://artsy.github.io/blog/2019/04/10/omakase-relay/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

When the mobile team at Artsy considered moving to React Native back in 2016, one of the most compelling cases for making that jump was Relay. This, it seems, is a dependency that is rarely used in the JS community and we often find ourselves re-explaining this decision to new engineers during onboarding, and to the public at large.

Which makes this a perfect blog post topic, so let's have a deep dive into what makes Relay compelling for Artsy's engineering team.

What problem does Relay solve?

Relay is an API client for GraphQL, it comes in two parts: a compiler and a set of front-end components. Relay aims to provide a really tight binding between your GraphQL API and your view hierarchy. When you build data-driven apps, Relay removes a whole suite of non-business logic from your application.

Relay handles:

  • Data binding (API → props)
  • Cache management (invalidation, re-render live components with data updates, etc)
  • Consistent abstractions for bi-directional pagination
  • Multiple query consolidation (e.g. consolidate all API requests to one request)
  • UI best practices baked in (e.g. optimistic response rendering)
  • Declarative data mutation (describe how data should change, instead of doing it)
  • Compile-time query generation (removing runtime overhead and allowing you to persist queries)

By taking the responsibilities of the grunt work for most complex apps and moving it into Relay you get Facebook-scale best-practices and can build on top of that.

How does it work?

You write a set of Relay components, you always start with a QueryRenderer and a tree of either FragmentContainer, RefetchContainer or PaginationContainers. You mostly use FragmentContainers, so I'll focus on that here.

A FragmentContainer is based on a GraphQL fragment. If you've never used a fragment, they are an abstraction that lets you declare shared field-selections on a specific GraphQL type to reduce duplication in your queries. For example:

query GetPopularArtistAndFeaturedArtist {
  featuredArtist {
    id
    name
    bio
  }
  popularArtist {
    id
    name
    bio
  }
}

To move this query to use fragments:

query GetPopularArtistAndFeaturedArtist {
  featuredArtist {
    ...ArtistMetadata
  }
  popularArtist {
    ...ArtistMetadata
  }
}

fragment ArtistMetadata on Artist {
  id
  name
  bio
}

It's a tiny bit longer, but you have a guarantee that the data is consistent across both artists. Now that you have a rough idea of what a GraphQL fragment is, let's look at what a FragmentContainer looks like. Here's a simplified profile page from the Artsy iOS app:

import React from "react"
import { createFragmentContainer, graphql } from "react-relay"
import { MyProfile_me } from "__generated__/MyProfile_me.graphql"

interface Props extends ViewProperties {
  me: MyProfile_me
}

export class MyProfile extends React.Component<Props> {
  render() {
    return (
      <View>
        <Header>
          <ProfilePhoto initials={this.props.me.initials} image={this.props.me.image} />
          <Subheading>{this.props.me.name}</Subheading>
        </Header>
        <ButtonSection>
          <ProfileButton
            section="Selling"
            description="Sell works from your collection"
            onPress={startSubmission}
          />
          <ProfileButton
            section="Account Details"
            description="Email, password reset, profile"
            onPress={goToUserSettings}
          />
        </ButtonSection>
      </View>
    )
  }
}

export default createFragmentContainer(MyProfile, {
  me: graphql`
    fragment MyProfile_me on Me {
      name
      image
      initials
    }
  `
})

There are three moving parts:

  • The TypeScript interface MyProfile_me, generated by the compiler, which ensures we can only use fields that were selected in the fragment
  • The MyProfile component, which is a vanilla React component
  • The exported createFragmentContainer which returns a higher-order component that wraps MyProfile and ties it to a fragment on a Me type in GraphQL

Isolation

The React component MyProfile will be passed in props that directly tie to the fragment that was requested. In Relay terms, this is called data masking and it is one of the first hurdles for someone new to Relay to grok. In REST clients, and GraphQL API clients like Apollo Client, you make a request and that request is passed through the React tree. E.g.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK