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.


    34. Add a Delete Ingredient Button

    Objective: Create a delete button for the GenerateIngredients component that will allow users to remove ingredients from the list.

    We need to create a delete button for each ingredient to ensure that users can remove unneeded ingredients from the list. We can add an additional column by concatinating a delete column onto the list of columns that we already have.

    components/GenerateIngredients.tsx

    import { Row, Col, Button, Table, Input, Dropdown } from 'antd';
    import { MenuList } from './MenuList';
    import * as _ from 'lodash';
    type IngredientsProps = {
    names?: string[];
    values?: [{ amount: string; unit: string; type: string }];
    handleAddIngredient: (event: any) => void;
    handleDeleteIngredient: (event: any) => void;
    handleInputChange: (event: any) => void;
    handleDropdownChange: (event: any) => void;
    };
    const units = ['-', 'ounce', 'lb', 'cup', 'tb', 'tsp', 'g', 'kg'];
    export const GenerateIngredients = ({
    names,
    values,
    handleAddIngredient,
    handleDeleteIngredient,
    handleInputChange,
    handleDropdownChange,
    }: IngredientsProps) => {
    const columns = _.concat(
    names.map((name) => ({
    title: `${name}`,
    key: `${name}`,
    render: (ingredient, _record, index: number) => {
    return name === 'unit' ? (
    <Dropdown
    overlay={
    <MenuList
    iterableList={units}
    name={`ingredients[${index}].${name}`}
    handleDropdownChange={handleDropdownChange}
    />
    }
    placement="bottomLeft"
    >
    <Button>{ingredient[name]}</Button>
    </Dropdown>
    ) : (
    <Input
    value={ingredient[name]}
    placeholder={`${name}`}
    name={`ingredients[${index}].${name}`}
    onChange={handleInputChange}
    />
    );
    },
    })),
    [
    {
    title: 'delete',
    key: 'delete',
    render: (_ingredient, _record, index: number) => (
    <Button
    onClick={handleDeleteIngredient}
    type="danger"
    shape="circle"
    size="small"
    name={`${index}`}
    >
    -
    </Button>
    ),
    },
    ],
    );
    return (
    <>
    <Row>
    <Col span={12} offset={6}>
    <p>
    <Button
    onClick={handleAddIngredient}
    type="primary"
    shape="circle"
    size="small"
    >
    +
    </Button>
    ingredients:
    </p>
    </Col>
    </Row>
    {values.length > 0 ? (
    <Row>
    <Col span={12} offset={6}>
    <Table
    dataSource={values}
    columns={columns}
    pagination={{ pageSize: 25 }}
    />
    </Col>
    </Row>
    ) : null}
    </>
    );
    };

    The delete button will call a delete ingredient handler, so we need to now create this in the submitForm hook and pass it into the <GenerateIngredients> component

    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 handleDropdownChange = (event) => {
    setInputs((inputs) => {
    const newInputs = _.cloneDeep(inputs);
    _.set(newInputs, event.item.props.title, event.key);
    return newInputs;
    });
    };
    const handleAddIngredient = (event) => {
    event.persist();
    setInputs((inputs) => {
    const sortedIngredients = _.sortBy(inputs.ingredients, ['key']);
    const key =
    sortedIngredients.length > 0
    ? sortedIngredients[sortedIngredients.length - 1].key + 1
    : 0;
    return {
    ...inputs,
    ingredients: _.concat(inputs.ingredients, [
    { key, amount: '', unit: '-', type: '' },
    ]),
    };
    });
    };
    const handleDeleteIngredient = (event) => {
    console.log('deleted');
    event.persist();
    const position = parseInt(event.target.name);
    setInputs((inputs) => ({
    ...inputs,
    ingredients: _.filter(
    inputs.ingredients,
    (_i, index) => index !== position,
    ),
    }));
    };
    const handleSubmit = () => {
    callback();
    setInputs(() => ({ ...initialValues }));
    };
    return {
    inputs,
    handleSubmit,
    handleInputChange,
    handleAddIngredient,
    handleDeleteIngredient,
    handleDropdownChange,
    };
    };