Frontend Serverless with React and GraphQL

Start at the beginning and work your way through this project. The code for each step as well as the finished project can be found in the Github repository.


35. Add a Form Submit Mutation

Objective: Add the create recipe mutation to the GenerateIngredients component so that we can make new recipes.

In this step we are going to create the createRecipe Mutation that we can use for submitting data to the backend. First we should add the useFetchUser hook to fetch the user information. We will use the user id so that we can assign an owner to the recipe when it is created. This will control which user is able to view the recipe in their my-recipes route as well as update or delete the recipe.

We use the useMutation hook to have a createRecipeMutation function that we can use for triggering the mutation after the submit button is pressed. While the mutation is loading, we are sure to disable the submit button so that users can't double click the button accidentally.

First, as a tiny cleanup issue from the last step, remove the console.log from utils/submitForm.ts in the handleDeleteIngredient function as we won't need it any more.

Next, let's get to building the createRecipeMutation function in the <CreateRecipe> component. After creating the function, you'll also want to add a disabled prop to the Create Recipe button. This will ensure it gets disabled when we are waiting for it to get submitted and someone won't accidentally double click on it and send 2 mutations. We also want to add a check for loading and show a <Loading/> component while the user information is still fetching.

components/CreateRecipe.tsx

import { Row, Col, Form, Button } from 'antd';
import { submitForm } from '../utils/submitForm';
import { GenerateInput, GenerateTextInput } from './GenerateFields';
import { GenerateIngredients } from './GenerateIngredients';
import { useMutation } from '@apollo/react-hooks';
import { createRecipeGraphQL } from '../graphql/mutations/createRecipe';
import { useFetchUser } from '../utils/user';
import * as _ from 'lodash';
import { Loading } from './notify/Loading';
import Router from 'next/router';
export const CreateRecipe = () => {
const [createRecipeMutation, { loading }] = useMutation(createRecipeGraphQL);
const { user, loading: isFetchingUser } = useFetchUser();
const owner = _.get(user, 'sub');
const initiateCreateRecipe = () => {
createRecipeMutation({
variables: {
data: {
...inputs,
owner,
},
},
});
Router.replace('/my-recipes');
};
const {
inputs,
handleInputChange,
handleAddIngredient,
handleDeleteIngredient,
handleDropdownChange,
handleSubmit,
} = submitForm(
{
title: '',
description: '',
content: '',
ingredients: [],
},
initiateCreateRecipe,
);
if (isFetchingUser) return <Loading />;
return (
<Form onFinish={handleSubmit}>
<GenerateInput
name="title"
value={inputs.title}
handleInputChange={handleInputChange}
/>
<GenerateInput
name="description"
value={inputs.description}
handleInputChange={handleInputChange}
/>
<GenerateTextInput
name="content"
value={inputs.content}
handleInputChange={handleInputChange}
/>
<GenerateIngredients
names={['amount', 'unit', 'type']}
values={inputs.ingredients}
handleAddIngredient={handleAddIngredient}
handleDeleteIngredient={handleDeleteIngredient}
handleInputChange={handleInputChange}
handleDropdownChange={handleDropdownChange}
/>
<Row>
<Col span={16} />
<Col span={4}>
<Form.Item label="Create Recipe">
<Button disabled={loading} type="primary" htmlType="submit">
Create Recipe
</Button>
</Form.Item>
</Col>
</Row>
</Form>
);
};