Relay Hooksと従来のコンテナAPI
Relay Hooksとコンテナの互換性
Relay HooksはRelayのコンテナベースのAPIと完全に互換性があります。つまり、コンテナはHooksを使用するコンポーネントをレンダリングでき、その逆も可能です。
これは、既存のアプリケーションの残りの部分に影響を与えることなく、新しいコードにのみ使用するか、アプリケーションの特定の部分を移行することにより、Relay Hooksを段階的に導入できることを意味します。
既存のコンテナベースのコードの移行
前述のとおり、既存のコードをRelay Hooksに移行する必要はありません。また、コンテナベースのコードは引き続き動作します。
ただし、このセクションでは、コンテナベースのコードをRelay Hooksに移行することを選択した場合に、従うことができる一般的な移行パターンについて説明します。
QueryRenderer → useLazyLoadQuery
QueryRendererからuseLazyLoadQuery Hookへの変換は、最も簡単な変換であり、レンダリング中に指定されたクエリをフェッチするという同様の動作になります。
QueryRendererをuseLazyLoadQueryに変換するには、次の手順を実行する必要があります。
- QueryRendererがあった場所、またはその上にRelayEnvironmentProviderをレンダリングします。通常は、アプリケーションのルートに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とError Boundariesによって処理されます。
<ErrorBoundary renderError={Error}>
  <Suspense fallback={<Loading />}>
    <Home />
  </Suspense>
</ErrorBoundary>
QueryRenderer → useQueryLoader + usePreloadedQuery
useLazyLoadQueryとは異なり、useQueryLoaderとusePreloadedQueryを組み合わせて使用すると、レンダリングの前にデータのフェッチが開始され、「render-as-you-fetch」パターンに従います。これは、データのフェッチがより早く開始され、ユーザーにコンテンツを表示するまでの時間が短縮される可能性があることを意味します。
このパターンを最大限に活用するには、通常、クエリロードをルーターレベル、またはUIインフラストラクチャの他の部分に統合します。完全な例については、issue-trackerサンプルアプリケーションを参照してください。
QueryRendererをuseQueryLoaderに変換するには、次の手順を実行する必要があります。
- QueryRendererがあった場所、またはその上にRelayEnvironmentProviderをレンダリングします。通常は、アプリケーションのルートに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
TODO(未対応)
commitMutation → useMutation
TODO(未対応)
requestSubscription → useSubscription
TODO(未対応)
このページはお役に立ちましたか?
いくつかの簡単な質問に答えることで、 サイトの改善にご協力ください。.