Rescript Relay connecting mutation with refechable pagination

While playing with Rescript Relay I encountered a problem with connections. That might be mostly Relay related problem but because I’m new to relay I’m not sure if I do something wrong with Rescript or if I’m lacking something on the Relay side.

Context:
I was trying to go through Rescript Relay tutorial and build a simple app.
I have a model that simplified SDL would look like this:

type Viewer {
  id: RelayNodeID!
  name: String!
  albums(
    after: String
    before: String
    first: Int
    last: Int
    filter: FilterAlbum
  ): AlbumConnection!
}

I use Rescript Relay Router to load queries and pass fragment refs to the page’s component where I use usePagination with refetch when I update filter value.
I went through Rescript Relay tutorial and added mutation and connections so my fragment looks like that:

module Fragment = %relay(`
  fragment AlbumRelayContent_albums on Viewer
  @refetchable(queryName: "AlbumRelayPaginationQuery")
  @argumentDefinitions(
    cursor: { type: "String" },
    count: { type: "Int", defaultValue: 100 }
    filter: { type: "FilterAlbum" }
  )
  {
    ...AlbumRelayComposerFragment
    albums(first: $count, after: $cursor, filter: $filter)
      @connection(key: "AlbumRelayAlbumsSection_albums") 
    {
      edges {
        node {
          id
          state
          ...AlbumRelayFragment_album
        }
      }
      totalCount
      pageInfo {
        hasNextPage
        startCursor
      }
    }
  }
`)

And here is my problem:
Everything works fine till I add a filter to usePagination’s refetch. With the connection a new album mutation updates the store and I can see a new album in UI as long as the filter is not set. With a filter, it displays filtered albums without a new one (even if it matches the filter).
What should I do to update the store for filtered values? At this point, I’m unsure if I should update the fragment, code, or maybe even cursor on BE?

If you use @prependEdge or @appendEdge in the mutation and pass it the connection ID, you should achieve exactly what you’re looking for.

You can follow rescript relay tutorial here.

I do:

module AddMutation = %relay(`
  mutation AlbumRelayAddMutation(
    $input: CreateAlbum!,
    $connections: [ID!]!
  ) {
    createAlbum(input: $input)
    @prependEdge(connections: $connections)  {
      node {
        ...AlbumRelayFragment_album
      }
    }
  }
`)

And as I mentioned it prepends as long as there is no filter. After mutation, I do have another edge. At the moment I use useEffect to run refetch:

  let {data: albums_pag, refetch} = Fragment.usePagination(viewerRef.viewer.fragmentRefs)
  let album_nodes = albums_pag.albums->Fragment.getConnectionNodes

  Js.log3("runnig before effect", filter, album_nodes->Array.length)
  React.useEffect2(() => {
    Js.log3("runnig effect", filter, album_nodes->Array.length)
    let filter_: option<AlbumRelaysPaginationQuery_graphql.Types.filterAlbum> = switch filter {
    | Some(s) => Some({state: s})
    | None => None
    }
    let variables = Fragment.makeRefetchVariables(~filter=filter_)
    refetch(~variables)->RescriptRelay.Disposable.ignore
    None
  }, (filter, album_nodes->Array.length))

So if I have the filter set to None and run mutation I can see the effect running. But if I set the filter before the mutation runs it will result in the same amount of nodes for filtered data. Switching the filter back to None and I can see the extra edge. It seems like with the filter set I get some not updated set of edges.

I don’t understand why you need a useEffect if you use @prependEdge or is it just to update the results when you change the filter? But then why adding album nodes length to the dependencies?

I apply the filter dynamically and I don’t know a better way to tell usePagination that I want to update it :sweat_smile:

do you have maybe a published repro or is your project open source?
How do you get the connection ID? Because the connection ID depends on the filter too.

1 Like

I use examples from tutorial (mutation step 4). In my example I use “composer”:

module AlbumRelayComposerFragment = %relay(`
  fragment AlbumRelayComposerFragment on Viewer {
    __id
  }
`)

I use it in Fragment module, and in code I get the connection like this:

  let {data: albumsRef, refetch} = Fragment.usePagination(viewerRef.viewer.fragmentRefs)
  let album_nodes = albumsRef.albums->Fragment.getConnectionNodes
  let (addAlbum, isAddingAlbum) = AddMutation.use()

  let {__id} = AlbumRelayComposerFragment.use(albumsRef.fragmentRefs)
  let connectionId = RescriptRelay.ConnectionHandler.getConnectionID(
    __id,
    "AlbumRelayAlbumsSection_albums",
    (),
  )

It’s a bet but because I use AlbumRelayComposerFragment.use I guess it’s not updated when pagination is refetched.
I can’t see how in my case connection is related to filter :thinking:
I don’t have repro at the moment but I can push it later today.

module Fragment = %relay(`
  fragment AlbumRelayContent_albums on Viewer
  @refetchable(queryName: "AlbumRelayPaginationQuery")
  @argumentDefinitions(
    cursor: { type: "String" },
    count: { type: "Int", defaultValue: 100 }
    filter: { type: "FilterAlbum" }
  )
  {
    ...AlbumRelayComposerFragment
    albums(first: $count, after: $cursor, filter: $filter)
      @connection(key: "AlbumRelayAlbumsSection_albums") 
    {
     __id // here is the connection ID
      edges {
        node {
          id
          state
          ...AlbumRelayFragment_album
        }
      }
      totalCount
      pageInfo {
        hasNextPage
        startCursor
      }
    }
  }
`)
let connectionId = albums_pag.albums.__id

Why don’t you directly get the connection ID from the fragment like this?

If I remember correctly, the connection ID changes when the parameters of the connection change (pagination, filters, etc), so it’s safer to get it this way. Try it, I’m pretty sure it would work this way.

1 Like

I think the reason is because I was blindly following the tutorial without prior Relay knowledge :sweat_smile:
I wrapped __id with AlbumRelayComposerFragment and placed it in the wrong place.
The order of things in the mutation chapter confused me a bit (StoryCommentsComposerFragment was used before it was defined) but well… I should be studying Relay now :smile:
Anyway… Thanks a lot for the help, it works now! :smile:

1 Like

The whole thing was definitely more of a relay issue than a rescript issue, but glad I could help :slight_smile: