Handle typing with data spread between main query and fragment reference in abstracted React components

41 views Asked by At

Based on the example of Relay, it seems trivial to implement a list that read the data from the main query and extract the fragment data from the story ref.

Relay example image

However, if we add a level of abstraction and need to deal with typing, it's a different story. Here is my example:

export default function Newsfeed({}) {
  const data = useLazyLoadQuery<NewsfeedQueryType>(NewsfeedQuery, {});
  const stories = data.topStories;
  return (
    <div className="newsfeed">
      <StoryList stories={stories} />
    </div>
  );
}

// The way I found is to extract the node type myself and later cast it to StoryFragment$key, which works but I feel like it's a trick
type StoryNode = NewsfeedQuery$data['topStories'][0]

function StoryList(stories: StoryNode[]) { // <<< HERE I'm using the extracted StoryNode to access the data from the main query, however my Story component expect a StoryFragment$key. Which seems accepted, as an implicit cast occurs, the compiler doesn't complain.
  return (
    <>
      {stories.map(story => (
        <Story
          key={story.id} // <<<<< HERE: I'd like to use data from the main query and not from the fragment
          storyRef={story} // <<< HERE: Looks like an implicit cast occurs to StoryFragment$key
        />
      )}
    </>
  );
}

function Story(storyRef: StoryFragment$key) {
  // Use fragment expect the generate type StoryFragment$key
  const story = useFragment(StoryFragment, storyRef)
  ...
}

The way I found is to extract the node type myself and later cast it to StoryFragment$key, which works, but I feel like it's a trick. Especially in a real case where usually the data looks more like:

topStories {
  edges {
     node {
       id
       ...StoryFragment
     }
  }
}

In that case we ends with:

type TopStories = NonNullable<NewsfeedQuery$data['topStories']>;
type StoryEdge = NonNullable<TopStories['edges']>[0];
type StoryNode = NonNullable<StoryEdge>['node'];

Which is probably ok, however the cast to the fragment ref bother me, and I feel like something is not right.

What would be the optimal way to handle the typing properly in that case?

0

There are 0 answers