relayjs / relay-examples

A collection of sample Relay applications
MIT License
1.14k stars 427 forks source link

[Newsfeed] Getting a type error when using readUpdatableFragment for StoryLikeButton #285

Open MrJackWilson opened 1 year ago

MrJackWilson commented 1 year ago

I'm getting the following type error when following the guide here: https://relay.dev/docs/tutorial/mutations-updates/#step-3--call-readupdatablefragment

Argument of type 'StoryLikeButtonFragment$key' is not assignable to parameter of type 'Readonly<{ ' $data'?: unknown; $updatableFragmentSpreads: unknown; }>'.
  Property '$updatableFragmentSpreads' is missing in type 'StoryLikeButtonFragment$key' but required in type 'Readonly<{ ' $data'?: unknown; $updatableFragmentSpreads: unknown; }>'`

Here is the entire onLikeButtonClicked function:

const onLikeButtonClicked = () => {
    commitMutation({
      variables: {
        id: data.id,
        doesLike: !data.doesViewerLike,
      },
      optimisticUpdater: (store) => {
        const fragment = graphql`
          fragment StoryLikeButton_updatable on Story @updatable {
            likeCount
            doesViewerLike
          }
        `;
        const { updatableData } = store.readUpdatableFragment_EXPERIMENTAL(
          fragment,
          story
        );
        const alreadyLikes = updatableData.doesViewerLike;
        updatableData.doesViewerLike = !alreadyLikes;
        updatableData.likeCount += alreadyLikes ? -1 : 1;
      },
    });
  };
davidmccabe commented 1 year ago

It might be helpful to put your code as a fork on github so that we can see the whole thing.

ckperez commented 1 year ago

I am stuck at the same point.

When running store.readUpdatableFragment(fragment, story) as recommended in the tutorial, I receive a type error of Property 'readUpdatableFragment' does not exist on type 'RecordSourceSelectorProxy<{}>' and a console error (on clicking like button) of:

Warning: A store update was detected within another store update. Please make sure new store updates aren't being executed within an updater function for a different update.

When running store.readUpdatableFragment(fragment, story), as this method appears to be available according to type checking, I see the type error mentioned by @MrJackWilson and the following console error (on clicking like button):

Warning: RelayModernSelector: Expected object to contain data for fragment `StoryLikeButton_updatable`, got `{"title":"Local Yak Named Yak of the Year","summary":"The annual Yak of the Year awards ceremony took place last night, and this year's winner is none other than Max, a beloved yak from the small town of Millville. Max, who is known for his friendly personality and hardworking nature, beat out stiff competition from other yaks in the region to take home the coveted title.\n \nAccording to the judges, Max stood out due to his exceptional contributions to the community. He has been used as a pac…`. Make sure that the parent operation/fragment included fragment `...StoryLikeButton_updatable` without `@relay(mask: false)`.

Problem line in fork

comatory commented 1 year ago

The types forces me to use readUpdatableFragment_EXPERIMENTAL method instead of readUpdatableFragment, I'm able to call readUpdatableFragment though so this is an issue with types.

But I also get error during runtime:

Warning: A store update was detected within another store update. Please make sure new store updates aren't being executed within an updater function for a different update.

When I switch to _EXPERIMENTAL method I get error:

RelayModernSelector: Expected object to contain data for fragment `StoryLikeButton_updatable`, got `{"title":"Local Yak Named Yak of the Year","summary":"The annual Yak of the Year awards ceremony took place last night, and this year's winner is none other than Max, a beloved yak from the small town of Millville. Max, who is known for his friendly personality and hardworking nature, beat out stiff competition from other yaks in the region to take home the coveted title.\n \nAccording to the judges, Max stood out due to his exceptional contributions to the community. He has been used as a pac…`. Make sure that the parent operation/fragment included fragment `...StoryLikeButton_updatable` without `@relay(mask: false)`.
Swahvay commented 5 months ago

With Relay v16, The _EXPERIMENTAL is gone, so now we are left with the RelayModernSelector error happening with every use of readUpdatableFragment. The docs example (https://relay.dev/docs/tutorial/mutations-updates/#step-3--call-readupdatablefragment) is still producing the error too.

JCMais commented 1 month ago

The only way I could make this work was by spreading the updatable fragment on the page query itself (looks like you cannot spread it on the component fragment?), and then doing some type-casting when running it, e.g:

        const fragment = graphql`
          fragment StoryLikeButton_updatable on Story @updatable {
            likeCount
            doesViewerLike
          }
        `;
        const { updatableData } = store.readUpdatableFragment<StoryLikeButton_updatable$key>(
          fragment,
          story as any
        );
        const alreadyLikes = updatableData.doesViewerLike;
        updatableData.doesViewerLike = !alreadyLikes;
        updatableData.likeCount += alreadyLikes ? -1 : 1;

Spreading the fragment on the query is what made the warnings go away, but I did not see this being mentioned in the page.

Spreading it on the fragment causes relay to throw an error saying the updatable fragment cannot be spread on the root.

Having to spread it on the root query feels wrong to me, and makes this basically break the composability of the fragments, as now the root query has to know about all the updates on the children components.

I am still trying to figure out how to update non-scalar values tho, as it seems the TS generated setters do not include the nested fields of the linked record?