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

Relay @ Jusbrasil

492a25871b1279286bdb95c7bacb7697?s=47 Helielson
January 30, 2019

Relay @ Jusbrasil

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

492a25871b1279286bdb95c7bacb7697?s=128

Helielson

January 30, 2019
Tweet

Transcript

  1. Relay @ A brief history about our experience with Relay

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

    Engineer at jusbrasil since 2010 > Tech Leader - Mobile Team @helielsonst
  3. Jusbrasil Mobile Team

  4. Our journey

  5. [photo @ jusbrasil] > Jusbrasil Alertas was our first React

    app March - 2015
  6. <App onCreateAlert={this.createAlert} /> <PageContainer onCreateAlert={onCreateAlert} /> <Page onCreateAlert={onCreateAlert} /> <Component

    onCreateAlert={onCreateAlert} />
  7. <App /> <PageContainer /> <Page /> <Component onCreateAlert={Actions.createAlert} /> >

    That time we used refluxjs to solve the prop down problem
  8. React is cool!!! Our thoughts after finishing the Alertas project:

  9. > We started the profile redesign with React. January -

    2016
  10. > 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
  11. { "name": "Helielson Santos", "username": "helielson", "isPro": true, "followers": [],

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

    { "me": { "fullname": "Helielson Santos", "username": "helielson", "isPro": true } } A GraphQL request/response
  13. const mutation = graphql` mutation CreateAlertMutation($input: CreateAlertInput!) { alert(input: $input)

    { term hasContent } } `; A "POST" like GraphQL request
  14. 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
  15. Launched!

  16. > We shared our experience with the dev team in

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

    > The projects are still using relay classic $ ag createContainer | wc -l 646
  18. journal view

  19. Online Office app

  20. > The online office app started as a vacation project

  21. It's official!!! > And became official in the first OKR

    of 2018
  22. > Before starting, we had to take some decisions

  23. Code reuse > How to reuse code? What to rewrite,

    what reuse?
  24. > We decided to rewrite the rendering logic because the

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

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

    to move the shared code to there
  27. > 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
  28. queries

  29. 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
  30. 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
  31. - createFragmentContainer - createRefetchContainer - createPaginationContainer > There are also

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

    become simpler. They are also static on Relay modern mutations
  33. > 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() {} }
  34. 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
  35. 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
  36. > 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'); });
  37. > The cache strategy changed from Relay classic to modern.

    Now it's up to us to enable cache. caching
  38. > 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 } }
  39. 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);
  40. > 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
  41. const configs = [{ type: confType, // RANGE_ADD, RANGE_DELETE, NODE_DELETE...

    }]; commitMutation(environment, { ...opts, updater: (store) => { // do anything with store eg. store.set() }, }); vs
  42. Relay compiler

  43. > 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
  44. > We need to overcome some challenges after changing to

    Relay modern Relay modern challenges
  45. > 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
  46. import { metricsFragment } from 'my-lib/lib/relay-query/classic'; export default Relay.createContainer(MyComponent, {

    fragments: { profile: () => Relay.QL` fragment on Profile { ${metricsFragment} } `, }, });
  47. 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) } `, });
  48. Compile published components

  49. 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) } `, });
  50. > 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
  51. What we have learned in our journey

  52. Each component should declare its own fragment

  53. const ProfileHeader = ({ me }) => ( <div> Name:

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

    {me.fullname} <ProfilePhoto profile={me} /> </div> ); fragment ProfileHeader_profile on Profile { me { fullname ...ProfilePhoto_profile } }
  55. Functions also declare fragments https://goo.gl/u1d33c

  56. const registerProfileView = (profile) => MetricsAPI.register(‘profileView’, { profileId: profile.id, profileUsername:

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

    profile.username, }); fragment ProfileHeader_profile on Profile { me { ...registerProfileViewFragment @relay(mask: false) } }
  58. Avoid to rewrite components

  59. 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
  60. Next steps

  61. Subscriptions Offline cache Relay 2.0 Next steps

  62. vagas@jusbrasil.com.br github.com/jusbrasil/careers

  63. @JusbrasilTech

  64. That's it! Thank you :)

  65. Gifs from giphy.com Images from unsplash.com Mockups from magicmockups.com Word

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