Skip to content

Incremental adoption API #711

@rbalicki2

Description

@rbalicki2

Incremental adoption API

Overview

  • We can migrate from leaf to root.
  • At the root, we can control whether to make an API call to fetch REST data or GraphQL with a parameter (i.e. which can be controlled by an experiment)
  • This is similar to how the relay migration API works.

Detailed migration example

  • So, imagine we have two components Root -> Leaf. We prop drill a Blog item down. We would first migrate Leaf, then Root. Let's talk each in turn

Migrating leaf

  • Leaf component: replace it with a field Blog.Leaf:
export const leaf = iso(`
  field Blog.Leaf($otherVar1: String!, $otherVar2: String!) @component {
    name
    someOtherField(otherVar1: $otherVar1, otherVar2: $otherVar2)
  }
`)(({ data, parameters }, { extraText }: { extraText: string }) => {
  // ...
})
  • In Root, call
<IsographMigrationRenderer
  field={iso(`reference Blog.Leaf(otherVar1: "foo")`)} /* any omitted parameters are required */
  parameters={{ otherVar2: "bar" }}
  runtimeProps={{ extraText: "baz" }}
  startUpdate={ /* optional, maybe unsupported at first */ }
  migrationContext={...} /* more on this later */
  legacyData={blog}
/>

There should be an equivalent hook-based API for non-@component's

  • If we have no hard coded parameters, babel/swc can change reference Blog.Leaf to the default export of the reader AST. If we do have hard-coded parameters, we have to figure out where to write the artifact! This is the same problem as anonymous entrypoints Anonymous entrypoints #712. At first, we can probably disallow hard-coded parameters.

Migrating Root

  • A new client field for Query.Root:
export const root = iso(`
  field Query.Root($id: ID!) {
    blog(id: $id) {
      title
      Leaf(otherVar1: "foo", otherVar2: "bar")
    }
  }
`)(({ data }) => ...)
  • Replace <IsographMigrationRenderer /> with a selection of Leaf and render that directly
  • In the Root React component (which was not deleted), call
const { fragmentReference } = useLazyMigrationReference(
  entrypoint(iso`entrypoint Query.Root`),
  /* variables */,
  /* options */,
  {
    strategy: 'Isograph', /* or 'Legacy' */
    migrationContext
  }
)

and render that as usual

migrationContext

The context contains at least two functions:

  • readScalarField(...)
  • readLinkedField(...)

I'm not sure if we only need two functions! We may need some more, such as subscribe, etc. (But subscriptions may just be started by read[Scalar|Linked]Field, though)

These functions allow you to read from the legacy data (e.g. reading blog.author_name when blog.authorName is requested)

Perhaps we can read migrationContext from React context, instead of passing it as a variable. But my strong preference is to be explicit. Folks can write wrappers in userland if they want to read it from context!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions