问题描述
我正在使用 React、Relay(实验性)和 Typescript 制作一个网络应用程序,但在跨不同片段重用具有相似数据/道具的组件时遇到了一些问题。
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'>
}
解决方法
在 Relay 中手动提取数据并将其作为 prop 传递给组件不是一个好习惯。相反,您应该在 ProfileIcon
组件上有一个片段,让 Relay 负责将相同的数据向下传递到您的组件。
因此您需要在 ProfileIcon 组件中创建一个片段并查询 profileImageUrl
,然后将此片段添加到 HomePage_currentUser
和 ProfilePage_currentUser
片段中,relay 将为您处理剩下的事情。
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 的新值。事实上,中继存储将使用变异有效负载上的新值进行更新,一旦存储中有新的更新,它就会将该数据传递给任何使用该数据的组件。