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.


29. Add a Form Input

Objective: Create a GenerateInput component that will allow for users to enter text that is saved into the form.

In the previous step we created the form that will contain all of the fields for creating a recipe. In this step we will create a <GenerateInput> component which can be used for allowing users to enter text inputs.

components/GenerateFields.tsx

import { Row, Col, Form, Input } from 'antd';
type InputType = {
name: string;
value: string;
handleInputChange?: (event: any) => void;
};
export const GenerateInput = ({
name,
value,
handleInputChange,
}: InputType) => (
<Row>
<Col span={12} offset={6}>
<Form.Item label={`${name}`}>
<Input
placeholder={`${name}`}
name={`${name}`}
value={value}
onChange={handleInputChange}
/>
</Form.Item>
</Col>
</Row>
);

The <Input> works as a controlled component because it receives its value from the value parameter and after a change has been made, we call an onChange handler which will take the updated contents of the input field and assigns it to a state that we are storing in the submitForm hook. We also need to add the handleInputChange function to the submitForm hook. This function will clone the current state of the form and update the field that changed with the new contents of the field. We ensure that the keys of the submitForm state object matches the name in the <Input> component so that we can easily know where to update the field when using the _.set() function call in the handleInputChange function.

utils/submitForm.ts

import { useState } from 'react';
import * as _ from 'lodash';
export const submitForm = (initialValues, callback) => {
const [inputs, setInputs] = useState(initialValues);
const handleInputChange = (event) => {
event.persist();
setInputs((inputs) => {
const newInputs = _.cloneDeep(inputs);
_.set(newInputs, event.target.name, event.target.value);
return newInputs;
});
};
const handleSubmit = () => {
callback();
setInputs(() => ({ ...initialValues }));
};
return {
inputs,
handleSubmit,
handleInputChange,
};
};

After creating the new <GenerateInput> component and updating the submitForm hook to handle form changes, we can actually use the <GenerateInput> in our form to manage the title parameter. We will show in the next step that adding additional parameters in recipe is as simple as calling the <GenerateInput> for each text input field.

components/CreateRecipe.tsx

import { Row, Col, Form, Button } from 'antd';
import { submitForm } from '../utils/submitForm';
import { GenerateInput } from './GenerateFields';
export const CreateRecipe = () => {
const initiateCreateRecipe = () => {
console.log('submitted form');
};
const { inputs, handleInputChange, handleSubmit } = submitForm(
{
title: '',
description: '',
},
initiateCreateRecipe,
);
return (
<Form onFinish={handleSubmit}>
<GenerateInput
name="title"
value={inputs.title}
handleInputChange={handleInputChange}
/>
<GenerateInput
name="description"
value={inputs.description}
handleInputChange={handleInputChange}
/>
<Row>
<Col span={16} />
<Col span={4}>
<Form.Item label="Create Recipe">
<Button type="primary" htmlType="submit">
Create Recipe
</Button>
</Form.Item>
</Col>
</Row>
</Form>
);
};