Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Relay @ Jusbrasil

Helielson
January 30, 2019

Relay @ Jusbrasil

A brief history about our experience with Relay and how we are handling classic and modern versions.

Helielson

January 30, 2019
Tweet

More Decks by Helielson

Other Decks in Technology

Transcript

  1. @helielson > Graduated from UNEB - Graphic Design > Software

    Engineer at jusbrasil since 2010 > Tech Leader - Mobile Team @helielsonst
  2. <App /> <PageContainer /> <Page /> <Component onCreateAlert={Actions.createAlert} /> >

    That time we used refluxjs to solve the prop down problem
  3. > We had experienced REST API to provide data to

    the Jusbrasil Alertas project. > While starting the profile project we saw GraphQL and Relay. It seemed promising
  4. { "name": "Helielson Santos", "username": "helielson", "isPro": true, "followers": [],

    "following": [] } /profile?username=helielson A REST request/response
  5. query ProfileHeader { me { fullname username isPro } }

    { "me": { "fullname": "Helielson Santos", "username": "helielson", "isPro": true } } A GraphQL request/response
  6. Server side > SEO is important for the profile section,

    so we checked the performance of server side rendering before launch. > It performed well after a patch on Relay (because of a memory leak in the server side) Rendering
  7. > We shared our experience with the dev team in

    our internal TEDs. The teams started to adopt the Relay/GraphQL Stack JusTalks
  8. > Amount of Relay containers we have across 10 projects

    > The projects are still using relay classic $ ag createContainer | wc -l 646
  9. > We decided to rewrite the rendering logic because the

    web mobile experience shouldn't be a responsive experience. It should be MOBILE web app
  10. - utils - validations - formatters > We decided to

    reuse the common non-rendering logic
  11. > We created a monorepo managed by Lerna and started

    to move the shared code to there
  12. > We also decided to use the modern version of

    Relay. I'll list the most different/valuable changes from Relay Classic to Modern Relay classic vs modern
  13. import { createContainer } from 'react-relay'; export default createContainer(ProfileHeader, {

    initialVariables: { size: null, }, fragments: { profile: () => Relay.QL` fragment on Profile { coverImage(size: $size) } `, }, }); > In the classic version queries and containers are distant from the real GraphQL queries syntax/mode
  14. import { createFragmentContainer, graphql } from 'react-relay'; export default createFragmentContainer(ProfileHeader,

    { profile: graphql` @argumentDefinitions(size: { type: "String!", defaultValue: "100x100" }) fragment ProfileHeader_profile on Profile { coverImage(size: $size) } `, }); > In the modern version queries became Static and similar to the GraphQL syntax
  15. - createFragmentContainer - createRefetchContainer - createPaginationContainer > There are also

    better HOC's focused on specific things. They reduce the boilerplate code to create pagination and refetch components
  16. > It was one of the most significant changes. Mutations

    become simpler. They are also static on Relay modern mutations
  17. > On Relay classic we needed to define a class

    to create a mutation import Relay from 'react-relay'; export default class CreateAlertMutation extends Relay.Mutation { getMutation() {} getVariables() {} getFatQuery() {} getConfigs() {} }
  18. import { createFragmentContainer, graphql } from 'react-relay'; const mutation =

    graphql` mutation CreateAlertMutation($input: CreateAlertInput!) { alert(input: $input) { term hasContent } } `; commitMutation({ mutation, variables: { input } }); > Now we simply declare a mutation query and commit it
  19. local schema > We are also using local schema and

    updates. Hence you can take advantage of the Relay store features for sharing the application state. It can easily replace Redux
  20. > You can either create or extend a type and

    update it in the Relay Store. The field is available to any component using the type as any other field extend type AlertItem { createdByModal: Boolean } commitLocalUpdate(relayEnv, proxy => { const itemRecord = proxy.get(itemDataID); itemRecord.setValue(true, 'createdByModal'); });
  21. > The cache strategy changed from Relay classic to modern.

    Now it's up to us to enable cache. caching
  22. > In Relay classic the cache is handled internally. So

    if you request a field in ComponentA, ComponentB won't request it again. > In Relay modern you need to create an instance of the cache. The queryId (name of the query) use to be the key of the record fragment ComponentA_profile on Profile { me { fullname } } fragment ComponentB_profile on Profile { me { fullname } }
  23. import { QueryResponseCache } from 'relay-runtime'; const cache = new

    QueryResponseCache({ size: size || 100, // 100 requests ttl: ttl || 15 * 60 * 1000, // 15 minutes }); cache.set(queryId, variables, response);
  24. > Mutation configs are hard to understand/handle. Relay modern still

    has it but also provide a updater that exposes the store. It's powerful because with the Store instance you can do anything from update a field to create a new edge/field. Mutation updater
  25. const configs = [{ type: confType, // RANGE_ADD, RANGE_DELETE, NODE_DELETE...

    }]; commitMutation(environment, { ...opts, updater: (store) => { // do anything with store eg. store.set() }, }); vs
  26. > There is a compiler in Relay modern responsible for

    merge the fields and generate code for the runtime. It does the hard work and let the runtime only with the cheap work - Generates runtime code - Hard work
  27. > We need to overcome some challenges after changing to

    Relay modern Relay modern challenges
  28. > We separated the fragments from its usage on the

    shared code (anti-pattern, I know, but it's temporary) in order to allow it to be used by projects using classic and modern Relay versions ./classicFragment.js ./modernFragment.js Backward compatibility
  29. import { metricsFragment } from 'my-lib/lib/relay-query/classic'; export default Relay.createContainer(MyComponent, {

    fragments: { profile: () => Relay.QL` fragment on Profile { ${metricsFragment} } `, }, });
  30. import { type modernMetricsFragment, } from 'my-lib/src/relay-query/__generated__/modernMetricsFragment.graphql'; export default createFragmentContainer(MyComponent,

    { root: graphql` fragment MyComponent_profile on RootApi { ...modernMetricsFragment @relay(mask: false) } `, });
  31. import { type modernMetricsFragment, } from 'my-lib/src/relay-query/__generated__/modernMetricsFragment.graphql'; export default createFragmentContainer(MyComponent,

    { root: graphql` fragment MyComponent_profile on RootApi { ...modernMetricsFragment @relay(mask: false) } `, });
  32. > We also added the shared code to the relay

    compiler lookup in order to allow it to generate the code ./node_modules/.bin/relay-compiler --src . \ --schema ./path-to-schema.json \ --include 'src/**' 'node_modules/my-lib/**' \ --exclude '**/__generated__/**' 'node_modules/my-lib/node_modules/**' \ --extensions js \ "$@" # allow to receive parameters
  33. const ProfileHeader = ({ me }) => ( <div> Name:

    {me.fullname} <ProfilePhoto profile={me} /> </div> ); fragment ProfileHeader_profile on Profile { me { fullname imagePath } } Unused here
  34. const ProfileHeader = ({ me }) => ( <div> Name:

    {me.fullname} <ProfilePhoto profile={me} /> </div> ); fragment ProfileHeader_profile on Profile { me { fullname ...ProfilePhoto_profile } }
  35. const registerProfileView = (profile) => MetricsAPI.register(‘profileView’, { profileId: profile.id, profileUsername:

    profile.username, }); fragment ProfileHeader_profile on Profile { me { id username } } Unused here
  36. const registerProfileView = (profile) => MetricsAPI.register(‘profileView’, { profileId: profile.id, profileUsername:

    profile.username, }); fragment ProfileHeader_profile on Profile { me { ...registerProfileViewFragment @relay(mask: false) } }
  37. At Jusbrasil we have an internal repository that holds our

    shared components. Most of them are Relay containers, which means they know how to load/use data. Reuse your components across projects
  38. Gifs from giphy.com Images from unsplash.com Mockups from magicmockups.com Word

    cloud from wordart.com Code styling from romannurik.github.io