BadgerPriest
BadgerPriest

Reputation: 733

What is the best practice for reusing components across fragments with React/Relay/Typescript?

I'm making a web app with React, Relay (experimental), and Typescript and I'm having some issues re-using components with similar data/props across different fragments.

Let's say I have the following Relay query and main app component:

const query = graphql`
  query AppQuery {
    currentUser {
      ...HomePage_currentUser
      ...ProfilePage_currentUser
    }
  }
`;

export default function App(): JSX.Element {
  const appData = useLazyLoadQuery<AppQuery>(query, {});
 
 return (
   <>
    <HomePage currentUser={appData.currentUser}>
    <ProfilePage currentUser={appData.currentUser}>
   </>
 );
}

And the following page components:

interface HomePageProps {
  currentUser: HomePage_currentUser$key | null;
}

const currentUserFragment = graphql`
  fragment HomePage_currentUser on User {
    id
    profileImageUrl
    items {
      id
      name
    }
  }
`;


export default function HomePage(props: HomePageProps): JSX.Element {
  const currentUser = useFragment(currentUserFragment, props.currentUser);

  return (
    <>
      {/* ... */}
      <ProfileIcon user={currentUser} >
      {/* ... */}
    </>
  )
}
interface ProfilePageProps {
  currentUser: ProfilePage_currentUser$key | null;
}

const currentUserFragment = graphql`
  fragment ProfilePage_currentUser on User {
    id
    profileImageUrl
    lastLoggedInTimestamp
  }
`;


export default function ProfilePage(props: ProfilePageProps): JSX.Element {
  const currentUser = useFragment(currentUserFragment, props.currentUser);

  return (
    <>
      {/* ... */}
      <ProfileIcon user={currentUser} >
      {/* ... */}
    </>
  )
}

And the following ProfileIcon component

interface ProfileIconProps {
  currentUser: ???
}

export default function ProfileIcon(props: ProfileIconProps): JSX.Element {
  return (
    <div>
      <img src={props.currentUser.profileImageUrl} />
    </div>
  )
}

My main question is regarding the type of the currentUser prop in the ProfileIcon component. It seems like there's no clean way to re-use the component for a ProfilePage_currentUser type and a HomePage_currentUser type despite the data being requested being incredibly similar and it clearly being compatible for the sake of this component.

Is there any recommended way to handle this other than something like this?

interface ProfileIconProps {
  currentUser: Omit<ProfilePage_currentUser, ' $refType'>
}

Upvotes: 3

Views: 1613

Answers (1)

Mateen Kajabadi
Mateen Kajabadi

Reputation: 3634

It's not a good practice in Relay to extract the data and pass it to a component as a prop manually. Instead you should have a fragment on the ProfileIcon component and let Relay takes care of passing the same data down to your component.

So you need to create a fragment in ProfileIcon component and query for profileImageUrl then add this fragment in both HomePage_currentUser and ProfilePage_currentUser fragments and relay will take care of the rest for you.

interface ProfileIconProps {
  currentUser: ProfileIcon_currentUser$key | null;
}

const currentUserFragment = graphql`
  fragment ProfileIcon_currentUser on User {
    profileImageUrl
  }
`;


export default function ProfileIcon(props: ProfileIconProps): JSX.Element {
  const currentUser = useFragment(currentUserFragment, props.currentUser);

  return (
    <div>
     <img src={props.currentUser.profileImageUrl} />
    </div>
  )
}

In HomePage component add ProfileIcon_currentUser fragment

const currentUserFragment = graphql`
 fragment HomePage_currentUser on User {
    id
    items {
      id
      name
    }
    ...ProfileIcon_currentUser
 }

`;

In ProfilePage component add ProfileIcon_currentUser fragment

 const currentUserFragment = graphql`
  fragment ProfilePage_currentUser on User {
    id
    lastLoggedInTimestamp
    ...ProfileIcon_currentUser
  }
`;

One of the advantage of using this pattern is that if you have a mutation on User somewhere else (even not within the same react node tree hierarchy) and let's say you change the profileImageUrl then the ProfileIcon will receive the new profileImageUrl automatically without you passing around a new value of profileImageUrl. In fact, Relay store will be updated with the new value on mutation payload and as soon as as there is a new update in store, it will pass that data to any components that are using that data.

Upvotes: 5

Related Questions