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"));
我有一个自定义文件上传字段,可以在您 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"));