relay-hooks

repository·master·Indexed 20 days ago

https://github.com/relay-tools/relay-hooks

A set of React hooks for using Relay features—including queries, fragments, and mutations—with a stable, non-experimental hook-based API. It provides support for offline-first policies and includes hooks such as useQuery for non-suspense queries, useLazyLoadQuery for Suspense-based queries, useFragment for data requirements, useMutation for committing mutations with optimistic updates, and usePagination for managing connections.

Tokens
17.3K
Snippets
52
Records
60
Agent score
67%

What's inside relay-hooks

  1. Compare loadLazyQuery and loadQuery for SSR preloading

    master

    When implementing SSR preloading in Relay Hooks, the choice between loadLazyQuery and loadQuery depends on whether you are using React Suspense:

    • With Suspense: Use loadLazyQuery from relay-hooks to prepare queries for a Suspense-enabled environment.
    • Without Suspense: Use loadQuery from relay-hooks for standard SSR preloading.
    // suspense
    import {loadLazyQuery} from 'relay-hooks';
    const prefetch = loadLazyQuery();
    
    // no suspense
    import {loadQuery} from 'relay-hooks';
    const prefetch = loadQuery();
  2. Implement Optimistic Updates with useMutation

    master

    To make your UI more responsive, you can use the Optimistic UI pattern by providing an optimisticResponse in the mutation options.

    When an optimisticResponse is provided, the data property of the useMutation hook will contain this optimistic data while the request is in flight (loading: true). Once the server responds and loading becomes false, the data property is updated to contain the actual data sent by the server.

    // Example concept of providing an optimistic response
    const [mutate, { data }] = useMutation(MyMutation, {
      optimisticResponse: {
        myMutation: {
          value: 'expected value',
        },
      },
    });
    
    // When mutate() is called:
    // 1. loading becomes true
    // 2. data becomes { myMutation: { value: 'expected value' } }
    // 3. Once server responds, loading becomes false
    // 4. data becomes the actual server response
  3. Differences between `useRefetchable` and `RefetchContainer`

    master

    If you are migrating from Relay's RefetchContainer, note the following improvements in useRefetchable:

    1. Automatic Query Generation: You no longer need to manually specify a refetch query; it is automatically generated by Relay using the @refetchable directive on the fragment.
    2. Simplified Variables: There is no longer a distinction between refetchVariables and renderVariables. Refetching always uses the provided variables, falling back to original parent query values for any omitted keys.
    3. Guaranteed Updates: Refetching via useRefetchable unequivocally updates the component, whereas RefetchContainer updates could depend on the specific query structure and object types.
  4. How RelayEnvironmentProvider works

    master

    Because useQuery does not set React context, you must wrap your application (or a specific subtree) in a RelayEnvironmentProvider. This component takes an environment prop and provides it to all descendant hooks. It should typically be rendered once at the root of your application.

    Note that variables are no longer part of the context provided by this component.

    import { RelayEnvironmentProvider } from 'relay-hooks';
    
    ReactDOM.render(
      <RelayEnvironmentProvider environment={modernEnvironment}>
        <AppTodo/>
      </RelayEnvironmentProvider>,
      rootElement,
    );
  5. Concept: Differences between usePagination and PaginationContainer

    master

    The usePagination hook is a modern replacement for the legacy PaginationContainer. Key differences include:

    • Automatic Query Generation: You no longer need to specify a pagination query; it is automatically generated via the @refetchable fragment.
    • Simplified Configuration: No need for getVariables, getFragmentVariables, direction, or getConnectionFromProps. Relay determines these automatically.
    • Variable Clarity: Removes the distinction between variables and fragmentVariables. Pagination requests use the original variables plus the necessary pagination variables.
    • Bi-directional Support: Supports simultaneous forward and backward pagination natively.
    • Reliable Refetching: Refetching unequivocally updates the component, whereas refetchConnection in PaginationContainer behavior could vary depending on the query structure.
  6. Develop the Relay Hooks TodoMVC example

    master

    The development environment supports hot reloading for application logic.

    • Application Logic: Changes made to files in the js/ directory will trigger an automatic rebuild and browser refresh.
    • Schema Changes: If you modify data/schema.js, you must manually regenerate the GraphQL schema and rebuild the app to reflect the changes. Use the following sequence:
    yarn run update-schema
    yarn run build
    yarn run start
  7. Run the Relay Hooks NextJS SSR TodoMVC example

    master

    To run the NextJS SSR TodoMVC example, you must first install the dependencies and then ensure the Relay generated files are set up before starting the development server.

    1. Install dependencies using yarn.
    2. Set up generated files by running yarn followed by yarn compile.
    3. Start the local development server with yarn dev.
    yarn
    yarn compile
    yarn dev
  8. Use the LoadQuery interface

    master

    A LoadQuery instance (returned by loadQuery or loadLazyQuery) provides the following methods to manage the lifecycle and data retrieval of a Relay query:

    • next(environment, gqlQuery, variables, options): Initiates the query execution. It resolves the query using the provided environment and variables. It returns a Promise<void> that rejects if the query triggers a suspense boundary or encounters an error.
    • getValue(environment?): Retrieves the current data. Depending on whether the loader was created via loadQuery or loadLazyQuery, it returns RenderProps, null, or a Promise.
    • subscribe(callback): Subscribes to updates. When the query data changes, the provided callback is executed. Returns a cleanup function to unsubscribe.
    • dispose(): Cleans up the internal QueryFetcher and resets the state.
    const loader = loadQuery();
    
    // 1. Start the query
    await loader.next(environment, MyQuery, { id: '123' });
    
    // 2. Get the data
    const data = loader.getValue(environment);
    
    // 3. Subscribe to updates
    const unsubscribe = loader.subscribe(() => {
      console.log('Data updated:', loader.getValue(environment));
    });
    
    // 4. Cleanup
    unsubscribe();
    loader.dispose();
  9. Understand Mutation State and Configuration

    master

    When using mutations, you can track the current state of the operation and configure its behavior.

    MutationState

    Represents the current lifecycle of a mutation:

    • loading: boolean indicating if the mutation is in progress.
    • data: The response data of type T['response'] or null.
    • error: An Error object or null if the mutation failed.

    MutationConfig

    Used to configure a mutation. It extends the base Relay MutationConfig but adds an onCompleted callback:

    • onCompleted?(response: T['response']): void: A callback executed when the mutation succeeds.
    export type MutationState<T extends MutationParameters> = {
        loading: boolean;
        data: T['response'] | null;
        error?: Error | null;
    };
    
    export type MutationConfig<T extends MutationParameters> = Partial<
        Omit<BaseMutationConfig<T>, 'mutation' | 'onCompleted'>
    > & {
        onCompleted?(response: T['response']): void;
    };