Skip to content

Client-only state #788

@rbalicki2

Description

@rbalicki2

We should support @localOnly state.

How it will behave

  • Fields can be marked as @localOnly.
  • If so, they will not be included in the network request. And, when the network response doesn't contain any data, we will not write null.
  • These fields can be written with startUpdate.
  • Can they be written with writeData, though? Presumably! But that's slightly awkward, because we have to distinguish between missing fields and null values when normalizing, and we have to express that in the rawResponseType.
  • I think the correct first move is to not allow you to write @localOnly fields in writeData.

Steps to implement

Denote fields as localOnly

  • In Relay, fields that are defined in schema extension files are automatically local-only. I don't like this. I think you should be able to use schema extension files to compose your schema from parts.
  • So, instead, we should use a directive @localOnly to mark a field as local-only.
  • We do something similar to to ExposeFieldDirective.

Add directive set to server scalar/object selectable

  • We have the opportunity to implement things in a pico-compatible fashion. So, ServerScalarSelectable and ServerObjectSelectable should have a parsed_directives field, which contains a Result<ServerScalarSelectableDirectiveSet, Diagnostic> or perhaps Result<ServerScalarSelectableDirectiveSet, Vec<Diagnostic>>. (Have separate structs for ServerScalarSelectableDirectiveSet and ServerObjectSelectableDirectiveSet)!
  • In other words, selectables that fail to parse due to having some garbage directives should not cause everything else to fail! This makes the language server more resilient.

Validation

  • In validate.rs, iterate through all of the selectables and ensure that the directives were successfully parsed.

Omit local-only fields from the merged selection map

  • The naive thing to do would be to just remove local-only fields from the merged selection map. That works for query text generation + writing the response into the store, but it does not work correctly for retaining in the presence of local-only linked fields.
  • However, it's substantially simpler without linked fields, and 95% of the value, so IMO a good first step. So, ServerObjectSelectableDirectiveSet should just be an empty object, i.e. it would fail t compile if you tried to slap localOnly on a linked field to an object
  • So, as a first step: when creating the merged selection map, omit any local-only fields. This should cause these fields to not show up in the query text and normalization AST, which is fine!

Modify type generation... or not

  • Naively, one might think that in param_type.ts, every local-only field should be nullable.
  • That's not true! That's only true because the "default constructor" of the field is () => null, and we provide no way to provide a different default constructor.
  • So, instead, we should (in validate.rs), also ensure that the type of each field contains the value of the default constructor. So, just ensure that the type of every local field is nullable.

Modify the ReaderScalarField and ReaderLinkedField etc to have a localOnly field

This could be broken up into separate PRs, if you wish:

  • PR 1: modify the types to have a localOnly field, land compiler changes to hard code that to false
  • PR 2: Modify read.ts to not suspend if a local-only field is missing.
  • PR 3: modify the compiler code to make the reader AST accurate (i.e. correctly say whether the field is local only)

startUpdate should just work.

Modify pet-demo

Have some local-only scalar state. Maybe a check box on a pet.

🎉 We did it! That's step 1 🎉


Future enhancements:

  • allow linked fields that are @localOnly. This will require modifying the normalization ASTs, since local state that points to a new record means that that new record must be retained as well. We use normalization ASTs for retaining.
  • think about raw response types + writeData and local state. In particular, does raw response type accept undefined for local state?
  • think about how to define default constructors for local state. This is dependent on [epic] Stateful resolvers (normalization-time resolvers) #335 .

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions