Relay 挂钩和传统容器 API
Relay 挂钩和容器之间的兼容性
Relay 挂钩与 Relay 的 基于容器的 API 完全兼容,这意味着容器可以渲染使用挂钩的组件,反之亦然。
这意味着您可以逐步采用 Relay 挂钩,要么专门用于新代码,要么迁移应用程序的特定部分,而不会影响现有应用程序的其余部分。
迁移现有的基于容器的代码
如前所述,将现有代码迁移到 Relay 挂钩是 非必需的,并且 基于容器的代码将继续工作。
但是,在本节中,我们将介绍如果您选择将基于容器的代码迁移到 Relay 挂钩,可以遵循的常见迁移模式。
QueryRenderer
→ useLazyLoadQuery
从 QueryRenderer
转换为 useLazyLoadQuery
挂钩是最直接的转换,并且将具有类似的行为,即在 渲染时 获取指定的查询。
要将 QueryRenderer
转换为 useLazyLoadQuery
,您需要执行以下步骤
- 渲染
RelayEnvironmentProvider
,其中包含 QueryRenderer,或者位于其上方。通常,我们建议在应用程序的最根部渲染RelayEnvironmentProvider
<RelayEnvironmentProvider environment={MyAppEnvironment}>
<App />
</RelayEnvironmentProvider>
- 将
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>
QueryRenderer
→ useQueryLoader
+ usePreloadedQuery
与 useLazyLoadQuery
不同,使用 useQueryLoader
结合 usePreloadedQuery
将在 渲染之前 开始获取数据,遵循“边获取边渲染”模式。这意味着数据获取将更快开始,并可能缩短向用户显示内容所需的时间。
为了充分利用这种模式,查询加载通常集成在路由级别或 UI 基础结构的其他部分。要查看完整示例,请参阅我们的 issue-tracker
示例应用程序。
要将 QueryRenderer
转换为 useQueryLoader
,您需要执行以下步骤
- 渲染
RelayEnvironmentProvider
,其中包含 QueryRenderer,或者位于其上方。通常,我们建议在应用程序的最根部渲染RelayEnvironmentProvider
<RelayEnvironmentProvider environment={MyAppEnvironment}>
<App />
</RelayEnvironmentProvider>
- 将
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
待办事项
此页面是否有用?
通过以下方式帮助我们改进网站 回答几个简短的问题.