DEV Community

Soichi Takamura
Soichi Takamura

Posted on â€ĸ Edited on

3

react-apollo + Codegen + TypeScript, How You Can Compose Multiple Queries/Mutations to a Component

Apollo team already provides decent documentation that lets you type your components with types generated from Codegen.

This is a quick tutorial to go further where you compose multiple GraphQL Query/Mutation to bind to a single component.

Summary

  • Define from outer HOCs (e.g. withData function) to inner HOCs
  • Pass with-data-props to a child HOC
  • Do not use compose() function

Example

SUPPOSE this is a new student creation form that requires pulldown schools list. Schools are resolved through GraphQL Query. Also, form submission is done through GraphQL Mutation.

// UserCreate.tsx

import {ChildDataProps, ChildMutateProps} from 'react-apollo';
import {
  CreateUserMutation,
  CreateUserMutationVariables,
} from './__generated__/CreateUserMutation';
import { SchoolsQuery } from './__generated__/SchoolsQuery';
import { CreateUserInput } from "../../../../__generated__/globalTypes";

type ComponentProps = { title: string, };
type WithDataProps = ChildDataProps<ComponentProps, SchoolsQuery, {}>;
const withData = graphql<ComponentProps, SchoolsQuery, {}, WithDataProps>(gql`
  query SchoolsQuery {
    schools {
      id
      email
      nameJa
      nameEn
      phone
    }
  }
`);

type WithMutateProps = ChildMutateProps<WithDataProps, CreateUserMutation, CreateUserMutationVariables>;
const withMutate = graphql<WithDataProps, CreateUserMutation, CreateUserMutationVariables, WithMutateProps>(gql`
  mutation CreateUserMutation($input: CreateUserInput!) {
    createUser(input: $input) {
      email
      nameEn
      nameJa
      school
    }
  }
`);

const UserCreate = withData(withMutate(props => {
  const {
    mutate: createUser,
    data: {schools},
  } = props;
  return (
    <Container>
      <h1>Create New User</h1>
      <Form
        method="post"
        onSubmit={async (e: any) => {
          e.preventDefault();

          const input = (createFormData(e.target) as CreateUserInput ) ;

          const result = await createUser({
            refetchQueries: [
              ({query: UsersQueryGQL} as PureQueryOptions)
            ],
            variables: {input},
          });
          if (!result || !result.data) throw new Error('Mutation failed.');

          const {
            data: {createUser: created},
          } = result;

          // Do something with created user data

          history.push('/users');
        }}
      >
        <FormGroup>
          <Label for="email">User email address:</Label>
          <Input id="email" type="text" name="email"/>
        </FormGroup>

        <FormGroup>
          <Label for="nameJa">User name (Japanese):</Label>
          <Input id="nameJa" type="text" name="nameJa"/>
        </FormGroup>

        <FormGroup>
          <Label for="nameEn">User name (English):</Label>
          <Input id="nameEn" type="text" name="nameEn"/>
        </FormGroup>

        {schools && (
          <>
            <FormGroup>
              <Label for="school">school</Label>
              <p>
                <select id="school" name="school">
                  <option>---please select---</option>
                  {schools.map(s => (
                    <option key={s.id} value={s.id}>
                      {s.nameEn}
                    </option>
                  ))}
                </select>
              </p>
            </FormGroup>
          </>
        )}

        <FormGroup>
          <Button color="primary" type="submit" block>
            Log in
          </Button>
        </FormGroup>
      </Form>
    </Container>
  );
}));

export default UserCreate;

In Outer HOC

type WithDataProps = ChildDataProps<ComponentProps, SchoolsQuery, {}>;
const withData = graphql<ComponentProps, SchoolsQuery, {}, WithDataProps>(/*...*/);
  • The first argument in generic arguments of graphql() is used as actual acceptable properties
  • The WithDataProps is used as types we pass to the next inner component
    • So this is what we should accept in the inner withMutate.

In Inner HOC

type WithMutateProps = ChildMutateProps<WithDataProps, CreateUserMutation, CreateUserMutationVariables>;
const withMutate = graphql<WithDataProps, CreateUserMutation, CreateUserMutationVariables, WithMutateProps>
  • Accept props WithDataProps, passed from the outer HOC withData
    • which already includes ComponentProps, so the final props the component consumes is compound of 3: Query data types, Mutation types and component own props.

compose function?

As of react-apollo version 2.5.2, compose is not typed.

export declare function compose(...funcs: Function[]): (...args: any[]) => any;

I wrote this post because I couldn't find any example to do that. Hope it helps someone.

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

DEV shines when you're signed in, unlocking a customized experience with features like dark mode!

Okay