@levels3d/offblast

Hooks and Suspense wrapper for Relay


Keywords
react, relay, graphql, hooks, suspense
License
MIT
Install
npm install @levels3d/offblast@1.0.3

Documentation

Offblast

Offblast is a helper library for Relay that provides React hooks to send GraphQL queries, mutations and subscriptions from functional components.

Context

Offblast uses the ReactRelayContext from react-relay to access the current Relay environment. Depending on how you use this library along with react-relay, you may need to add a <Provider /> component higher in the React tree to use the hooks.

Suspender

Suspender<T> is a utility class provided by this library that acts as a wrapper for a Promise<T>, with the ability to query its result synchronously once it has resolved (or its error if it has rejected).

This is useful as a common pattern with GraphQL and Relay is to merge all the data needed by a page in a single query, but you don't necessarily want to show a single loading indicator for the whole page but only in the places where data is missing. The Suspender<T> class exposes a map method with the signature map<U>(func: (value: T) => U): Suspender<U> to help you narrow a query result to a specific bit of data while passing the Suspender object down the props of the components to the point where the data is actually needed, which is where a loading indicator should be displayed.

Hooks

useQuery

The useQuery hook has the following signature:

function useQuery<T extends OperationBase>(
  query: GraphQLTaggedNode,
  variables?: T["variables"],
  skip?: "execute" | "lookup" | boolean,
  inputs: any[]
): Suspender<T["response"]>;

This hook executes the GraphQL query specified in query with the variables optionally specified in variables, and returns a Suspender that will eventually resolve to the result of said query.

useQuery will also subscribe to updates of the Relay cache, so your components will automatically re-render when the data previously returned by the hook has changed.

Please note that the hook does NOT check if your variables or your query has changed since the last render: instead, is uses the same pattern as native hooks such as useEffect or useMemo and exposes an input: any[] parameter that is used to detect whether the query should be executed again.

Finally, the hooks has a skip for advanced use cases where you want to partially or completely skip the query execution. Since the Rule of Hooks forbids hooks in if branches, setting this argument to true will skip the execution of the query and return a Suspender<null> that resolves immediately. Alternatively, you can set the skip parameter to 'lookup' to skip the Relay cache lookup and force the query to be sent to the server, or 'execute' to skip the execution of the query and only query the Relay cache, possibly returning incomplete data.

useMutation

The useMutation hook has the following signature:

function useMutation<T extends OperationBase, A extends any[]>(
  config: (...args: A) => MutationConfig<T>,
  inputs: any[]
): (...args: A) => Cancelable<T["response"]>;

It is essentially a wrapper for useCallback, and returns a function that will in turn call commitMutation with the configuration you provided. Just like useMemo it takes 2 parameters: config is a function that returns a MutationConfig object, and inputs is an arbitrary array that drives the memoization of the returned function. The Cancelable<T> returned by the callback function is a Promise<T> with an additional cancel(): void method that will dispose of the internal operation.

useSubscription

The useSubscription hook has the following signature:

function useSubscription<T extends OperationBase>(
  config: SubscriptionConfig<T>,
  inputs: any[]
): void;

The config object describes the subscription to be created, and the inputs array is used to determine whether to configuration has changed and the subscription should be recreated. Note that this hook does not return anything: it is expected that your configuration will use an updater function to mutate the Relay store in response to subscription updates which will automatically trigger a re-render of any query that uses the modified values.

useEnvironment

The useEnvironment hook has the following signature:

function useEnvironment(): Environment;

It simply returns the current Relay environment, or throw an error if there is none (usually this is because you need to have a <Provider /> component higher in the React tree).

useResult

The useResult hook has the following signature:

function useResult<T>(value: Suspender<T>): T | null;

This hook unwraps a Suspender object. If it has already resolved, the result is returned and can be used immediately. If it has rejected, the error is thrown and can be caught using the componentDidCatch lifecycle method. Finally, if it hasn't resolved yet, the hooks will return null and subscribe to it in order to trigger a re-render when it will eventually resolve.

useSuspender

The useSuspender hook has the following signature:

function useSuspender<T>(value: Suspender<T>): T | null;

This hook unwraps a Suspender object. If it has already resolved, the result is returned and can be used immediately. If it has rejected, the error is thrown and can be caught using the componentDidCatch lifecycle method. Finally, if it hasn't resolved yet, the hooks will return null. Note that unlike useResult, this hooks does NOT subscribe to the Suspender and will NOT trigger a re-render if it resolves after having previously returned null.

useSuspense

The useSuspense hook has the following signature:

function useSuspense<T>(value: Suspender<T>): T;

This hook unwraps a Suspender object. If it has already resolved, the result is returned and can be used immediately. If it has rejected, the error is thrown and can be caught using the componentDidCatch lifecycle method. Finally, if it hasn't resolved yet, the Suspender's internal Promise will be thrown which will cause the current React tree (up to the nearest <React.Suspense /> component) to become suspended.

Please note that React Suspense is NOT STABLE yet, please use this hook at your own risk.