跳到主要内容

介绍 Relay Hooks

·阅读时间:6 分钟

我们非常兴奋地发布Relay Hooks,这是迄今为止最适合开发者的 Relay 版本,以及将它提供给开源社区!Relay Hooks 是一套全新的、经过重新设计的 API,用于使用 React Hooks 获取和管理 GraphQL 数据。

新的 API 与现有的基于容器的 API 完全兼容。虽然我们建议使用 Relay Hooks 编写所有新代码,但将现有的容器迁移到新的 API 是可选的,基于容器的代码将继续工作

虽然这些 API 是新发布的,但它们并非没有经过测试:重写的Facebook.com完全由 Relay Hooks 驱动,这些 API 自 2019 年年中以来一直是 Facebook 推荐的 Relay 使用方式。

此外,我们还发布了重写的引导教程更新的文档,这些文档总结了我们自最初开发 Relay 以来学到的构建可维护、数据驱动的应用程序的最佳实践。

虽然我们还有很长的路要走才能让 Relay 的入门变得像我们希望的那样简单,但我们相信这些步骤将使 Relay 的开发者体验变得更好。

发布了什么?

我们发布了 Relay Hooks,一套基于 React Hooks 的 API,用于处理 GraphQL 数据。我们还借此机会发布其他改进,例如fetchQuery 的更稳定版本,以及使用 getDataID 在 Relay 中自定义对象标识符的能力(如果你的服务器没有全局唯一的 ID,这将非常有用)。

请参阅发布说明,以获取发布内容的完整列表。

Hooks API 的优势是什么?

新发布的 API 至少在以下方面改善了开发体验。

  • 用于获取查询、使用片段加载数据、分页、重新获取、突变和订阅的基于 Hooks 的 API 通常比等效的基于容器的解决方案需要更少的代码行,并且间接性更低。
  • 这些 API 具有更完整的 Flow 和 Typescript 覆盖范围。
  • 这些 API 利用编译器功能来自动执行容易出错的任务,例如生成重新获取和分页查询。
  • 这些 API 附带配置提取策略的能力,这让你可以确定从存储中满足查询以及执行网络请求的条件。
  • 这些 API 使你能够在组件渲染之前开始获取数据,这是基于容器的解决方案无法实现的。这允许更快地向用户显示数据。

以下示例演示了新 API 的一些优势。

使用不同变量重新获取片段

首先,让我们看看如何使用 Hooks API 使用不同的变量重新获取片段。

type Props = {
comment: CommentBody_comment$key,
};

function CommentBody(props: Props) {
const [data, refetch] = useRefetchableFragment<CommentBodyRefetchQuery, _>(
graphql`
fragment CommentBody_comment on Comment
@refetchable(queryName: "CommentBodyRefetchQuery") {
body(lang: $lang) {
text
}
}
`,
props.comment,
);

return <>
<CommentText text={data?.text} />
<Button
onClick={() =>
refetch({ lang: 'SPANISH' }, { fetchPolicy: 'store-or-network' })
}>
>
Translate
</Button>
</>
}

将其与等效的基于容器的示例进行比较。基于 Hooks 的示例需要更少的代码行,不需要开发者手动编写重新获取查询,对重新获取变量进行类型检查,并明确说明如果可以从存储中的数据中满足查询,则不应发出网络请求。

在渲染组件之前开始获取数据

新的 API 允许开发者通过在组件渲染之前开始获取数据来更快地向用户显示内容。使用这种方式预取数据是基于容器的 API 无法实现的。请考虑以下示例。

const UserQuery = graphql`
query UserLinkQuery($userId: ID!) {
user(id: $userId) {
user_details_blurb
}
}
`;

function UserLink({ userId, userName }) {
const [queryReference, loadQuery] = useQueryLoader(UserQuery);

const [isPopoverVisible, setIsPopoverVisible] = useState(false);

const maybePrefetchUserData = useCallback(() => {
if (!queryReference) {
// calling loadQuery will cause this component to re-render.
// During that re-render, queryReference will be defined.
loadQuery({ userId });
}
}, [queryReference, loadQuery]);

const showPopover = useCallback(() => {
maybePrefetchUserData();
setIsPopoverVisible(true);
}, [maybePrefetchUserData, setIsPopoverVisible]);

return <>
<Button
onMouseOver={maybePrefetchUserData}
onPress={showPopover}
>
{userName}
</Button>
{isPopoverVisible && queryReference && (
<Popover>
<React.Suspense fallback={<Glimmer />}>
<UserPopoverContent queryRef={queryReference} />
</React.Suspense>
</Popover>
)}
</>
}

function UserPopoverContent({queryRef}) {
// The following call will Suspend if the request for the data is still
// in flight:
const data = usePreloadedQuery(UserQuery, queryRef);
// ...
}

在本示例中,如果无法从本地缓存中的数据中满足查询,则在用户将鼠标悬停在按钮上时将发起网络请求。因此,当最终按下按钮时,用户将更快地看到内容。

相比之下,基于容器的 API 在组件渲染时会发起网络请求。

用于数据获取的 Hooks 和 Suspense

你可能已经注意到,这两个示例都使用了 Suspense。

虽然 Relay Hooks 在一些 API 中使用了 Suspense,但React 中对用于数据获取的 Suspense 的支持、一般指南和使用要求仍然 尚未准备好,React 团队仍在定义即将发布的版本将提供哪些指南。在 React 17 中使用 Suspense 时,存在一些限制。

尽管如此,我们现在发布了 Relay Hooks,因为我们知道这些 API 正朝着支持即将发布的 React 版本的方向发展。即使 Relay 的 Suspense 实现的某些部分可能会发生变化,但 Relay Hooks API 本身是稳定的;它们已在内部得到广泛采用,并且已经在生产环境中使用了超过一年。

请参阅Suspense 兼容性使用 Suspense 加载状态,以深入了解此主题。

下一步做什么?

请查看入门指南迁移指南引导教程

感谢

发布 Relay Hooks 不仅仅是 React 数据团队的功劳。我们要感谢所有帮助我们实现这一目标的贡献者。

@0xflotus, @AbdouMoumen, @ahmadrasyidsalim, @alexdunne, @alloy, @andrehsu, @andrewkfiedler, @anikethsaha, @babangsund, @bart88, @bbenoist, @bigfootjon, @bondz, @BorisTB, @captbaritone, @cgriego, @chaytanyasinha, @ckknight, @clucasalcantara, @damassi, @Daniel15, @daniloab, @earvinLi, @EgorShum, @eliperkins, @enisdenjo, @etcinit, @fabriziocucci, @HeroicHitesh, @jaburx, @jamesgeorge007, @janicduplessis, @jaroslav-kubicek, @jaycenhorton, @jaylattice, @JonathanUsername, @jopara94, @jquense, @juffalow, @kafinsalim, @kyarik, @larsonjj, @leoasis, @leonardodino, @levibuzolic, @liamross, @lilianammmatos, @luansantosti, @MaartenStaa, @MahdiAbdi, @MajorBreakfast, @maraisr, @mariusschulz, @martinbooth, @merrywhether, @milosa, @mjm, @morrys, @morwalz, @mrtnzlml, @n1ru4l, @Nilomiranda, @omerzach, @orta, @pauloedurezende, @RDIL, @RicCu, @robrichard, @rsmelo92, @SeshanPillay25, @sibelius, @SiddharthSham, @stefanprobst, @sugarshin, @taion, @thedanielforum, @theill, @thicodes, @tmus, @TrySound, @VinceOPS, @visshaljagtap, @Vrq, @w01fgang, @wincent, @wongmjane, @wyattanderson, @xamgore, @yangshun, @ymittal, @zeyap, @zpao 和 @zth。

开源项目relay-hooks 允许社区尝试 Relay 和 React Hooks,并为我们提供了宝贵的反馈。useSubscription Hook 的想法起源于该仓库中的一个问题。感谢 @morrys 推动了这个项目,并在我们的开源社区中发挥了如此重要的作用。

感谢你帮助我们实现这一切!