How to update the state of with multiple elements by their key in React?

Viewed 54

I am trying to submit a form that has 4 form fields.

export type ChapterData = {
  chapterNumber: number;
  volumeNumber?: number;
  chapterName?: string;
  chapterImages: ImageListType;
};

I have made a custom hook, useAddChapterForm.

The initial state is as:

const initialState: ChapterData = {
  chapterNumber: 0,
  volumeNumber: 0,
  chapterName: '',
  chapterImages: [],
};

I want to update the state of all these elements in a single state and have used this approach.

  const [chapterData, setChapterData] = useState<ChapterData>(initialState);
  const changeHandler = (e: ChangeEvent<HTMLInputElement>) => {
    setChapterData({ ...chapterData, [e.target.name]: e.target.value });
  };

But I want to handle the change of the chapterImages differently. So, my hook looks like so.

export const useAddChapterForm = () => {
  const [chapterData, setChapterData] = useState<ChapterData>(initialState);
  const [images, setImages] = useState([]);
  const maxNumber = 69;

  const onChangeImageHandler = (imageList: ImageListType, addUpdateIndex: number[] | undefined) => {
    console.log(imageList, addUpdateIndex);
    setImages(imageList as never[]);
    setChapterData({ ...chapterData, [chapterData.chapterImages]: imageList });
  };

  const changeHandler = (e: ChangeEvent<HTMLInputElement>) => {
    setChapterData({ ...chapterData, [e.target.name]: e.target.value });
  };

  return {
    chapterData,
    maxNumber,
    changeHandler,
    images,
    onChangeImageHandler,
  };
};

I want to set the value of chapterImages when there is a change in with onChangeImageHandler so I am able to remove the images too.

    setChapterData({ ...chapterData, [chapterData.chapterImages]: imageList });

error

There is a typescript error. So, how do I change the state of this key chapterData.chapterImageswith setState?

Updated the question as per suggested answer:

  const onChangeImageHandler = (imageList: ImageListType, addUpdateIndex: number[] | undefined) => {
    console.log(imageList, addUpdateIndex);
    setImages(imageList as never[]);
    setChapterData({ ...chapterData, chapterImages: imageList });
  };

  const changeHandler = (e: ChangeEvent<HTMLInputElement>) => {
    setChapterData({ ...chapterData, [e.target.name]: e.target.value });
  };

Here is handle submit:

  const { chapterData, changeHandler } = useAddChapterForm();
  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    console.log(chapterData);
  };
  return (
    <form onSubmit={handleSubmit}>
    ...

I am using this component to upload the image.


export const ImageUploadField: FunctionComponent<ImageUploadFieldProps> = (
  props: ImageUploadFieldProps
) => {
  const { error } = props;
  const { images, onChangeImageHandler, maxNumber } = useAddChapterForm();
  return (
    <FieldWrapper label={'Select cover image'} errorMessage={error}>
      <ImageUploading multiple value={images} onChange={onChangeImageHandler} maxNumber={maxNumber}>
        {({
          imageList,
          onImageUpload,
          onImageRemoveAll,
          onImageUpdate,
          onImageRemove,
          isDragging,
          dragProps,
        }) => (
          <div className="outline-dashed outline-2 outline-offset-2">
            {imageList.length === 0 && (
              <button
                className="w-full text-center"
                style={isDragging ? { color: 'red' } : undefined}
                onClick={onImageUpload}
                {...dragProps}
              >
                Click or Drop here
              </button>
            )}
            <div className="flex flex-col justify-center">
              {imageList.length > 0 && (
                <div className="px-2 py-1 rounded bg-red-500 flex">
                  <button className=" space-x-2 w-full" onClick={onImageRemoveAll}>
                    Remove all images
                  </button>
                  <XCircle />
                </div>
              )}
              <div className="flex flex-wrap">
                {imageList.map((image, index) => (
                  <div key={index} className="flex">
                    <div className="flex flex-col p-2">
                      <Image
                        src={image.dataURL as string}
                        alt={image.file?.name}
                        width={150}
                        height={200}
                        quality={65}
                        className="rounded-tl rounded-bl"
                      />
                      <p>{image.file?.name}</p>
                      <div className="flex space-x-4">
                        <div className="flex px-2 py-1 rounded bg-green-500 space-x-2">
                          <button onClick={() => onImageUpdate(index)}>Update</button>
                          <PencilEdit />
                        </div>
                        <div className="flex px-2 py-1 rounded bg-red-500 space-x-2">
                          <button onClick={() => onImageRemove(index)}>Remove</button>
                          <XCircle />
                        </div>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        )}
      </ImageUploading>
    </FieldWrapper>
  );
};

submitform

1 Answers

You dont have to use computed property name for this line

setChapterData({ ...chapterData, [chapterData.chapterImages]: imageList });

You can just use this setter function like this

setChapterData({ ...chapterData, chapterImages: imageList });
Related