React Hooks – Text Inputs and Store Inputs in an array

The exercise model looks like this.

{
 amount: "",
 reps: [],
 id: "",
};

I’m having difficulty storing the reps array from the text inputs and saving it into State

enter image description here

I think the problem is the onChanged method, setting the reps array value to the corresponding text inputs.

  // update exercises with new amount
  const onChanged = (e) => {
    const exerciseIndex = e.target.dataset.index;
    const updatedExercises = [...exercises];

    updatedExercises[exerciseIndex].amount = e.target.value;
    updatedExercises[exerciseIndex].reps = e.target.value;

    setExercises(updatedExercises);
  };
...
  Amount
  <TextInput
    value={exercise.amount}
    data-index={index}
    onChange={onChanged}
  ></TextInput>
  <TableCell>
    <Flex justifyContent={"center"}>1</Flex>
    <TextInput
      value={exercise.reps[0]}
      data-index={index}
      onChange={onChanged}
    ></TextInput>
  </TableCell>
  <TableCell>
    <Flex justifyContent={"center"}>2</Flex>
    <TextInput
      value={exercise.reps[1]}
      data-index={index}
      onChange={onChanged}
    ></TextInput>
  </TableCell>
    ...

Saving the amount value is working correctly.

Whole page snippet is here.

// @ts-nocheck
import { React, useState, useEffect } from "react";
import {
  Button,
  EditorToolbarButton,
  SkeletonBodyText,
  SkeletonContainer,
  Table,
  TableBody,
  TableRow,
  TableCell,
  TextInput,
  Paragraph,
  RadioButton,
  SectionHeading,
  Flex,
  RadioButtonField,
} from "@contentful/forma-36-react-components";
import { FieldExtensionSDK } from "contentful-ui-extensions-sdk";

interface FieldProps {
  sdk: FieldExtensionSDK;
}

const ExercisesList = (props: FieldProps) => {
  const fieldValue = props.sdk.field.getValue();
  const [exercises, setExercises] = useState(fieldValue || []);


  // use contentful's builtin auto-resizer
  useEffect(() => {
    props.sdk.window.startAutoResizer();
  });

  // check for unresolved names and fetch them from contenful if neccessary
  useEffect(() => {
    const exercisesWithoutName = exercises.filter((exercise) => !exercise.name);
    if (!exercisesWithoutName.length) {
      return;
    }

    const ids = exercises.map((exercise) => exercise.id);
    props.sdk.space.getEntries({ "sys.id[in]": ids }).then((queryResult) => {
      let populatedExercises = exercises.map((exercise) => {
        const resultForCurrentExercise = queryResult.items
          .filter((entry) => entry.sys.id === exercise.id)
          .pop();

        return {
          name: resultForCurrentExercise
            ? resultForCurrentExercise.fields.title["en-US"]
            : "",
          ...exercise,
        };
      });
      setExercises(populatedExercises);
    });
  }, [exercises, props.sdk.space]);

  // update contentful field value whenever exercises data changes
  useEffect(() => {
    const sanitizedExercises = exercises.map((exercise) => {
      console.log("exercise ", exercise);
      return {
        amount: exercise.amount,
        reps: [exercise.reps],
        id: exercise.id,
      };
    });
    props.sdk.field.setValue(sanitizedExercises);
  }, [exercises, props.sdk.field]);

  // open entry selection dialog and append selected entries to the end of our list
  const onAddButtonClicked = () => {
    props.sdk.dialogs
      .selectMultipleEntries({ contentTypes: ["exercise"] })
      .then((selectedExercises) => {
        setExercises([
          ...exercises,
          ...selectedExercises.map((exercise) => {
            console.log("...exercises", ...exercises);
            return {
              amount: "",
              reps: [""],
              id: exercise.sys.id,
              key: `${exercise.sys.id}-${Math.floor(Math.random() * 100000)}`,
            };
          }),
        ]);
        props.sdk.field.setValue(exercises);
      })
      .catch(() => {
        /* do nothing */
      });
  };

  // update exercises with new amount
  const onChanged = (e) => {
    const exerciseIndex = e.target.dataset.index;
    const updatedExercises = [...exercises];

    updatedExercises[exerciseIndex].amount = e.target.value;
    updatedExercises[exerciseIndex].reps = e.target.value;

    setExercises(updatedExercises);
  };

  // remove exercise from list
  const onDeleteButtonClicked = (e) => {
    let actualTarget = e.target;
    while (!actualTarget.dataset.index || actualTarget.id === "root") {
      actualTarget = actualTarget.parentNode;
    }
    if (actualTarget.id === "root") {
      return;
    }
    const exerciseIndex = parseInt(actualTarget.dataset.index);
    const updatedExercises = exercises.filter(
      (_, index) => index !== exerciseIndex
    );
    setExercises(updatedExercises);
  };

  return (
    <section>
      <div>
        <Table>
          <TableBody>
            {exercises.map((exercise, index) => {
              return (
                <>
                  <TableRow key={exercise.key}>
                    <TableCell style={{ width: "150px" }}>
                      {exercise.name ? (
                        exercise.name
                      ) : (
                        <SkeletonContainer svgHeight="20">
                          <SkeletonBodyText numberOfLines="1"></SkeletonBodyText>
                        </SkeletonContainer>
                      )}
                    </TableCell>
                    Amount
                    <TextInput
                      value={exercise.amount}
                      data-index={index}
                      onChange={onChanged}
                    ></TextInput>
                    <TableCell style={{ width: "50px" }}>
                      <Flex flexDirection={"column"} justifyContent={"center"}>
                        <Paragraph
                          style={{ marginTop: "30px", textAlign: "right" }}
                        >
                          Reps(x)
                        </Paragraph>
                      </Flex>
                    </TableCell>
                    <TableCell>
                      <Flex justifyContent={"center"}>1</Flex>
                      <TextInput
                        value={exercise.reps[0]}
                        data-index={index}
                        onChange={onChanged}
                      ></TextInput>
                    </TableCell>
                    <TableCell>
                      <Flex justifyContent={"center"}>2</Flex>
                      <TextInput
                        value={exercise.reps[1]}
                        data-index={index}
                        onChange={onChanged}
                      ></TextInput>
                    </TableCell>
                    <TableCell>
                      <Flex justifyContent={"center"}>3</Flex>
                      <TextInput
                        value={exercise.reps[2]}
                        data-index={index}
                        onChange={onChanged}
                      ></TextInput>
                    </TableCell>
                    <TableCell>
                      <Flex justifyContent={"center"}>4</Flex>
                      <TextInput
                        value={exercise.reps[3]}
                        data-index={index}
                        onChange={onChanged}
                      ></TextInput>
                    </TableCell>
                    <TableCell>
                      <Flex justifyContent={"center"}>5</Flex>
                      <TextInput
                        value={exercise.reps[4]}
                        data-index={index}
                        onChange={onChanged}
                      ></TextInput>
                    </TableCell>

                
                    <TableCell>
                      <SectionHeading element="h5">Intensity</SectionHeading>
                      <Flex marginRight={"spacing2Xl"} alignItems="center">
                        <RadioButtonField
                          labelText="Maximum"
                          name="someOption1"
                          // checked={activeOption === "yes"}
                          value="yes"
                          // onChange={(e) => {
                          //   setActiveOption(
                          //     (e.target as HTMLInputElement).value
                          //   );
                          // }}
                          id="termsCheckbox"
                        />
                      </Flex>
                      <Flex alignItems="center">
                        <RadioButton
                          id="Checkbox"
                          labelText="some label text"
                          name="some-name"
                        />
                        <TextInput placeholder="%1RM" />
                      </Flex>
                    </TableCell>
                    <TableCell>
                      <EditorToolbarButton
                        icon="Delete"
                        data-index={index}
                        onClick={onDeleteButtonClicked}
                      ></EditorToolbarButton>
                    </TableCell>
                  </TableRow>
                </>
              );
            })}
          </TableBody>
        </Table>
      </div>
      <div style={{ marginTop: "10px", marginBottom: "10px" }}>
        <Button icon="Plus" buttonType="naked" onClick={onAddButtonClicked}>
          Add
        </Button>
      </div>
    </section>
  );
};

export default ExercisesList;

119 thoughts on “React Hooks – Text Inputs and Store Inputs in an array”