react-final-form 中的字段可以将自己标记为无效/阻止提交吗?

Can a field in react-final-form mark itself as invalid / prevent submission?

我有一个自定义文件上传字段,可以在您 select/drop 文件后立即上传文件,returns 一个 UUID 供以后提交。所以,当你放下一个文件时,基本上就是现在大多数网络应用程序(例如 Facebook、Twitter 等)所做的事情。

使用 final-form 处理这些都很容易 - 我的字段只需在上传完成后调用 final-form 的 onChange 函数,将 UUID 传递给 final-form。

但是,如果用户提交表单 而上传仍在 运行 他们将提交没有文件 UUID 的表单,因为就最终表单而言有关,尚未选择任何文件。特别是对于较大的文件,这将是一个问题,因为用户可能没有意识到他们仍然需要等待(即使有加载指示器)。将字段标记为必填也不是一个选项,因为根本不提供文件是有效的(或者该字段可能允许多个文件,或者您正在替换以前上传的文件) - 所以该字段的唯一情况是 "invalid" 是当前正在上传文件的时间。

这是一个带有小型虚拟应用程序的 codesandbox,它应该为任何尝试解决它的人提供一个很好的起点:https://codesandbox.io/s/polished-fast-k80t7

想法是单击 "Pretend to start uploading" 时字段无效,单击 "Pretend to finish uploading" 后再次有效。

请注意,我正在寻找一种干净的方法来做到这一点,同时保持事物分离,即我不希望将此状态添加到包含 Form 的组件 - 也因为验证函数需要是幂等的,所以检查外部状态会很糟糕(如我的 attempt of doing this 所示)。


如果 codesandbox link 有问题,这里是第一个 link 的相关代码(因为另一个只是一个失败的尝试):

import React, { useState } from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Form, Field } from "react-final-form";

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const onSubmit = async values => {
  await sleep(300);
  window.alert(JSON.stringify(values, 0, 2));
};

const MyFileUploader = ({ input: { value, onChange }, meta: { invalid } }) => {
  const [isUploading, setUploading] = useState(false);
  const handleStartClick = () => {
    setUploading(true);
  };
  const handleFinishClick = () => {
    setUploading(false);
    onChange("0xdeadbeef"); // let's pretend this is the file UUID ;)
  };
  const style = { color: invalid ? "#f00" : "#000" };
  if (value) {
    return <em style={style}>{value}</em>;
  } else if (isUploading) {
    return (
      <button type="button" onClick={handleFinishClick} style={style}>
        Pretend to finish uploading
      </button>
    );
  } else {
    return (
      <button type="button" onClick={handleStartClick} style={style}>
        Pretend to start uploading
      </button>
    );
  }
};

const App = () => (
  <Styles>
    <h1>React Final Form</h1>
    <Form
      onSubmit={onSubmit}
      initialValues={{ file: null }}
      render={({ handleSubmit, form, submitting, values }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>File</label>
            <Field name="file" component={MyFileUploader} />
          </div>
          <div className="buttons">
            <button type="submit" disabled={submitting}>
              Submit
            </button>
            <button type="button" onClick={form.reset} disabled={submitting}>
              Reset
            </button>
          </div>
          <pre>{JSON.stringify(values, 0, 2)}</pre>
        </form>
      )}
    />
  </Styles>
);

render(<App />, document.getElementById("root"));

有趣的问题。

这样的事情怎么样?上传文件时,渲染一个永远无效的字段,从而阻止提交。

const SubmitBlocker = ({ children }) => (
  <Field name="uploading" validate={() => children}>
    {({ meta }) =>
      meta.touched && meta.error ? meta.error : null
    }
  </Field>
);