无法使用 React hook 表单获取上传图像值

Can't get upload image value by using React hook form

我正在尝试构建一个可以上传多张图片的表单。
可以在预览图片期间删除图片。

例如,我上传了两张图片,然后我尝试点击删除按钮删除两张正常工作的图片之一。

问题是在我单击提交按钮期间假设将 post 一张图片发送到 API 但仍然有两张图片 post 发送到 API。
我正在使用 React hook 表单,有人知道问题是什么吗? 这是项目链接 https://codesandbox.io/s/boring-hermann-xrqbt

const methods = useFormContext();
const { control, watch, setValue, formState } = methods;
const [image, setImage] = useState([]);

function deleteFile(id) {
  const s = image.filter((item, index) => item.id !== id);
  setImage(s);
}

<div className="flex justify-center sm:justify-start flex-wrap -mx-16">
  <Controller
    name="images"
    control={control}
    render={({ field: { onChange, value } }) => (
      <label
        htmlFor="button-file"
        className={clsx(
          classes.productImageUpload,
          "flex items-center justify-center relative w-128 h-128 rounded-16 mx-12 mb-24 overflow-hidden cursor-pointer shadow hover:shadow-lg"
        )}
      >
        <input
          accept="image/*"
          className="hidden"
          id="button-file"
          type="file"
          onChange={async (e) => {
            function readFileAsync() {
              return new Promise((resolve, reject) => {
                const file = e.target.files[0];
                if (!file) {
                  return;
                }
                const reader = new FileReader();

                reader.onload = () => {
                  resolve({
                    id: FuseUtils.generateGUID(),
                    url: `data:${file.type};base64,${btoa(reader.result)}`,
                    type: "image",
                  });
                };

                reader.onerror = reject;

                reader.readAsBinaryString(file);
              });
            }

            const newImage = await readFileAsync();

            setImage([newImage, ...image]);
            onChange([newImage, ...image]);
          }}
        />
        <Icon fontSize="large" color="action">
          cloud_upload
        </Icon>
      </label>
    )}
  />
  <Controller
    name="featuredImageId"
    control={control}
    defaultValue=""
    render={({ field: { onChange, value } }) =>
      image.map((media) => (
        <div
          onClick={() => onChange(media.id)}
          onKeyDown={() => onChange(media.id)}
          role="button"
          tabIndex={0}
          className={clsx(
            classes.productImageItem,
            "flex items-center justify-center relative w-128 h-128 rounded-16 mx-12 mb-24 overflow-hidden cursor-pointer outline-none shadow hover:shadow-lg",
            media.id === value && "featured"
          )}
          key={media.id}
        >
          <Icon
            className={classes.productImageFeaturedStar}
            onClick={() => deleteFile(media.id)}
          >
            Delete
          </Icon>
          <img
            className="max-w-none w-auto h-full"
            src={media.url}
            alt="product"
          />
        </div>
      ))
    }
  />
</div>;


出于安全原因,您不能使用 type="file" 以编程方式设置输入的值。

<input
  ...
  type="file"
  onChange={uploadimg}
  // remove the value prop
/>

readFileAsync 函数中,您需要考虑即将到来的多个文件,因此我们将一个 File 传递给它。

您正在连接 FileReader 的结果以获得有效的 Base64 字符串,但您可以只使用 FileReader#readAsDataURL。该函数将 return 图像的 Base64 字符串。

function readFileAsync(file) {
  ...
    reader.onload = () => {
      resolve({
        id: uuid(),
        url: reader.result,
        type: "image",
      });
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

并且在uploadimg函数中,

async function uploadimg(e) {
  const file = e.target.files[0];
  if (file) {
    setImage([...images, (await readFileAsync(file))]);
    setFiles([...files, file]);
  }
}

deleteFiles函数中,现在我们必须更新imagefiles状态。

因为在files状态下,图像没有任何独特的记录,所以我们使用插入位置(from the uploadimg function)来匹配文件并删除要删除的文件。

function deleteFile(id) {
  const imageIndex = images.findIndex(item => item.id === id);

  if (imageIndex > -1) {
    setImages(images.filter(item => item.id !== id));
    setFiles(files.filter((_, i) => i !== imageIndex));
  }
}

}


为了节省

不需要 react-hook-form 包,因为无法以编程方式设置具有 type="file" 值的输入。

因此,如果它是您要访问的输入中的文件

  • 您可以使用 ref 访问它
  • 以另一种状态存储文件
  • 使用您生成的 Base64 个字符串。

我使用了另一种状态来保存 CodeSandbox 中的文件,但您可以使用这三个选项中的任何一个。