首页 > 解决方案 > 使用 React/Relay/Typescript 跨片段重用组件的最佳实践是什么?

问题描述

我正在使用 React、Relay(实验性)和 Typescript 制作一个 Web 应用程序,并且在跨不同片段重用具有相似数据/道具的组件时遇到了一些问题。

假设我有以下中继查询和主要应用程序组件:

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}>
   </>
 );
}

以及以下页面组件:

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} >
      {/* ... */}
    </>
  )
}

以及以下ProfileIcon组件

interface ProfileIconProps {
  currentUser: ???
}

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

我的主要问题是关于组件中currentUser道具的类型。尽管请求的数据非常相似并且显然为了这个组件而兼容,但ProfileIcon似乎没有干净的方法可以将组件重新用于ProfilePage_currentUser类型和类型。HomePage_currentUser

除了这样的事情之外,还有什么推荐的方法来处理这个问题吗?

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

标签: reactjstypescriptrelay

解决方案


在 Relay 中,手动提取数据并将其作为道具传递给组件并不是一个好习惯。相反,您应该在ProfileIcon组件上有一个片段,并让 Relay 负责将相同的数据向下传递给您的组件。

因此,您需要在 ProfileIcon 组件中创建一个片段并查询,profileImageUrl然后在两者中添加此片段,HomePage_currentUser片段ProfilePage_currentUser和中继将为您处理其余的工作。

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>
  )
}

在 HomePage 组件中添加ProfileIcon_currentUser片段

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

`;

在 ProfilePage 组件中添加ProfileIcon_currentUser片段

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

使用此模式的一个优点是,如果您在其他地方对 User 进行了突变(即使不在同一个反应节点树层次结构中)并且假设您更改了,profileImageUrl那么 ProfileIcon 将profileImageUrl自动接收新的,而无需您传递新的profileImageUrl 的值。实际上,中继存储将使用突变有效负载上的新值进行更新,并且一旦存储中有新的更新,它就会将该数据传递给使用该数据的任何组件。


推荐阅读