跳至主要内容
版本:v18.0.0

Relay 挂钩和传统容器 API

Relay 挂钩和容器之间的兼容性

Relay 挂钩与 Relay 的 基于容器的 API 完全兼容,这意味着容器可以渲染使用挂钩的组件,反之亦然。

这意味着您可以逐步采用 Relay 挂钩,要么专门用于新代码,要么迁移应用程序的特定部分,而不会影响现有应用程序的其余部分。

迁移现有的基于容器的代码

如前所述,将现有代码迁移到 Relay 挂钩是 必需的,并且 基于容器的代码将继续工作

但是,在本节中,我们将介绍如果您选择将基于容器的代码迁移到 Relay 挂钩,可以遵循的常见迁移模式。

QueryRendereruseLazyLoadQuery

QueryRenderer 转换为 useLazyLoadQuery 挂钩是最直接的转换,并且将具有类似的行为,即在 渲染时 获取指定的查询。

要将 QueryRenderer 转换为 useLazyLoadQuery,您需要执行以下步骤

  1. 渲染 RelayEnvironmentProvider,其中包含 QueryRenderer,或者位于其上方。通常,我们建议在应用程序的最根部渲染 RelayEnvironmentProvider
<RelayEnvironmentProvider environment={MyAppEnvironment}>
<App />
</RelayEnvironmentProvider>
  1. QueryRenderer 转换为 useLazyLoadQuery

之前

import * as React from 'React';
import {graphql, QueryRenderer} from 'react-relay';

export default function Home() {
return (
<QueryRenderer
environment={MyAppEnvironment}
query={graphql`
query HomeQuery($id: ID!) {
user(id: $id) {
name
}
}
`}
variables={{id: 4}}
render={(props, error) => {
if (error) {
return <Error />;
}
if (!props) {
return <Loading />;
}
return <h1>{props.user?.name}</h1>
}}
/>
);
}

之后:获取并渲染查询

import * as React from 'React';
import {graphql, useLazyLoadQuery} from 'react-relay';

export default function Home() {
const data = useLazyLoadQuery(
graphql`
query HomeQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
{id: 4},
);

return <h1>{data.user?.name}</h1>;
}

加载状态错误状态 由 Suspense 和错误边界处理

<ErrorBoundary renderError={Error}>
<Suspense fallback={<Loading />}>
<Home />
</Suspense>
</ErrorBoundary>

QueryRendereruseQueryLoader + usePreloadedQuery

useLazyLoadQuery 不同,使用 useQueryLoader 结合 usePreloadedQuery 将在 渲染之前 开始获取数据,遵循“边获取边渲染”模式。这意味着数据获取将更快开始,并可能缩短向用户显示内容所需的时间。

为了充分利用这种模式,查询加载通常集成在路由级别或 UI 基础结构的其他部分。要查看完整示例,请参阅我们的 issue-tracker 示例应用程序。

要将 QueryRenderer 转换为 useQueryLoader,您需要执行以下步骤

  1. 渲染 RelayEnvironmentProvider,其中包含 QueryRenderer,或者位于其上方。通常,我们建议在应用程序的最根部渲染 RelayEnvironmentProvider
<RelayEnvironmentProvider environment={MyAppEnvironment}>
<App />
</RelayEnvironmentProvider>
  1. QueryRenderer 转换为 useQueryLoader + usePreloadedQuery

之前

import * as React from 'React';
import {graphql, QueryRenderer} from 'react-relay';

export default function UserPopover() {
return (
<QueryRenderer
environment={MyAppEnvironment}
query={graphql`
query UserPopoverQuery($id: ID!) {
user(id: $id) {
name
}
}
`}
variables={{id: 4}}
render={(props, error) => {
if (error) {
return <Error />;
}
if (!props) {
return <Loading />;
}
return <h1>{props.user?.name}</h1>
}}
/>
);
}

之后:渲染预加载的查询

import * as React from 'React';
import {graphql, usePreloadedQuery} from 'react-relay';

export default function UserPopover(props) {
const data = usePreloadedQuery(
graphql`
query UserPopoverQuery($id: ID!) {
user(id: $id) {
name
}
}
`,
props.queryRef,
);

return <h1>{data.user?.name}</h1>;
}

使用 useQueryLoader 中的 loadQuery 加载查询。此代码部分通常会集成到您的路由或 UI 基础结构的其他部分

import * as React from 'React';
import {useQueryLoader} from 'react-relay';

// Import the query defined in the UserPopover component
import UserPopoverQuery from '__generated__/UserPopoverQuery.graphql';

// This is *NOT* a real-world example, only used
// to illustrate usage.

export default function UserPopoverButton(props) {
const [queryRef, loadQuery] = useQueryLoader(UserPopoverQuery)

const handleClick = useCallback(() => {
// Load the query in the event handler, onClick
loadQuery({id: props.userID})
}, [loadQuery, props.userID]);

return (
<>
<Button onClick={handleClick} />
{queryRef != null ?
<Popover>

{/* Loading and error states are handled by
Suspense and Error Boundaries */}
<ErrorBoundary renderError={Error}>
<Suspense fallback={<Loading />}>

{/*Pass the queryRef*/}
<UserPopover queryRef={queryRef} />

</Suspense>
</ErrorBoundary>
</Popover>
: null
}
</>
);
}

片段容器 → useFragment

片段容器将直接映射到 useFragment 调用

之前

import * as React from 'React';
import {graphql, createFragmentContainer} from 'react-relay';

function UserComponent(props: Props) {
const user = props.user;
return (
<>
<h1>{user.name}</h1>
<div>
<img src={user.profile_picture?.uri} />
</div>
</>
);
}

export default createFragmentContainer(UserComponent, {
user: graphql`
fragment UserComponent_user on User {
name
age
profile_picture(scale: 2) {
uri
}
}
`,
});

之后

import * as React from 'React';
import {graphql, useFragment} from 'react-relay';

export default function UserComponent(props: Props) {
const data = useFragment(
graphql`
fragment UserComponent_user on User {
name
profile_picture(scale: $scale) {
uri
}
}
`,
props.user,
);

return (
<>
<h1>{data.name}</h1>
<div>
<img src={data.profile_picture?.uri} />
</div>
</>
);
}

重新获取容器 → useRefetchableFragment

用于 useRefetchableFragment 的重新获取 API 与以前的重新获取容器相比已经简化和减少。迁移将需要将输入映射到新的 API 中。

之前

import * as React from 'React';
import {graphql, createRefetchContainer} from 'react-relay';

function CommentBody(props: Props) {
const relay = props.relay;

return (
<>
<p>{data.body?.text}</p>
<Button
onClick={() => relay.refetch(
{lang: 'SPANISH'}, // fragmentVariables
null, // renderVariables
error => { ... },
{force: true}
)}>
Translate Comment
</Button>
</>
);
}

export default createRefetchContainer(
CommentBody,
{
user: graphql`
fragment CommentBody_comment on Comment {
body(lang: $lang) {
text
}
}
`,
},

// This option is no longer required, the refetch query
// will automatically be generated by Relay using the @refetchable
// directive.
graphql`
query AppQuery($id: ID!, lang: Lang) {
node(id: $id) {
...CommentBody_comment
}
}
`,
);

之后

import * as React from 'React';
import {graphql, useRefetchableFragment} from 'react-relay';

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

const handleClick = useCallback(() => {
refetch({lang: 'SPANISH'});
}, [refetch]);

return (
<>
<p>{data.body?.text}</p>
<Button
onClick={handleClick}>
Translate Comment
</Button>
</>
);
}

分页容器 → usePaginationFragment

用于 usePaginationFragment 的分页 API 与以前的 PaginationContainer 相比已经大大简化和减少。迁移将需要将输入映射到新的 API 中。

之前

import * as React from 'React';
import {graphql, createPaginationContainer} from 'react-relay';

class UserContainerComponent extends React.Component {
render(): React.Node {
const isLoading = this.props.relay.isLoading() || this.state.loading;
const hasMore = this.props.relay.hasMore();

return (
<>
<FriendsList friends={this.props.user?.friends} />
<Button
onClick={() => this.loadMore()}
disabled={!hasMore || isLoading}>
Load More
{isLoading && <InlineSpinner />}
</Button>
</>
);
}

loadMore() {
if (
!this.props.relay.hasMore() ||
this.props.relay.isLoading() ||
this.state.loading
) {
return;
}

this.setState({loading: true});

this.props.relay.loadMore(5, () => this.setState({loading: false}));
}
}

export default createPaginationContainer(
UserContainerComponent,
{
user: graphql`
fragment UserContainerComponent_user on User
@argumentDefinitions(count: {type: "Int!"}, cursor: {type: "ID"})
@refetchable(queryName: "UserComponentRefetchQuery") {
friends(first: $count, after: $cursor)
@connection(key: "UserComponent_user_friends") {
edges {
node {
name
}
}
}
}
`,
},
{
// This option is no longer necessary, usePaginationFragment supports
// bi-directional pagination out of the box.
direction: 'forward',

// This option is no longer required, and will be automatically
// determined by usePaginationFragment
getConnectionFromProps(props: Props) {
return props.user?.friends;
},

// This option is no longer required, and will be automatically
// determined by usePaginationFragment
getFragmentVariables(vars, count) {
return {...vars, count};
},

// This option is no longer required, and will be automatically
// determined by usePaginationFragment
getVariables(props: Props, {count, cursor}) {
return {
cursor,
count,
};
},

// This option is no longer required, the pagination query
// will automatically be generated by Relay using the @refetchable
// directive.
query: graphql`
query UserContainerComponentQuery {
viewer {
actor {
... on User {
...UserContainerComponent_user @arguments(count: 10)
}
}
}
}
`,
},
);

之后

import * as React from 'React';
import {graphql, usePaginationFragment} from 'react-relay';

export default function UserComponent(props: Props) {
const {data, loadNext, hasNext, isLoadingNext} = usePaginationFragment(
graphql`
fragment UserComponent_user on User
@refetchable(queryName: "UserComponentRefetchQuery") {
friends(first: $count, after: $after)
@connection(key: "UserComponent_user_friends") {
edges {
node {
name
}
}
}
}
`,
props.user,
);

const handleClick = useCallback(() => {
loadNext(5)
}, [loadNext])

return (
<>
<FriendsList friends={data?.friends?.edges} />
<Button onClick={handleClick} disabled={!hasNext || isLoadingNext}>
Load More
{isLoadingNext && <InlineSpinner />}
</Button>
</>
);
}

QueryRenderer → useEntryPointLoader + EntryPointContainer

待办事项

commitMutation → useMutation

待办事项

requestSubscription → useSubscription

待办事项


此页面是否有用?

通过以下方式帮助我们改进网站 回答几个简短的问题.