如何将 FilePond 与 react-final-form 一起使用?

How to use FilePond with react-final-form?

我正在尝试让 FilePond 与 react-final-form 一起工作。我不确定如何将 FilePond 组件包装在 react-final-form 字段组件适配器中。

例如,对于 react-phone-number-input 我是这样做的:

// Before React.Component class declaration.
const PhoneAdapter = ({ input }) => (
    <PhoneInput
    placeholder="Phone Number"
    value={input.value}
    tabIndex="3"
    id="contact-form-id-phone"
    onChange={value => input.onChange(value)}
  />
);

// Inside React.Component class declaration.
<Field name="phone" component={PhoneAdapter} />

对于 FilePond,我试过这个:

// Before React.Component class declaration.
const FileAdapter = ({ input }) => (
  <FilePond
    // files={files}
    allowMultiple
    // onupdatefiles={setFiles}
    server="/api/upload"
    labelIdle="Drag &amp; Drop your files or <span class=&quot;filepond--label-action&quot;>Browse</span>."
    />
);

// Inside React.Component class declaration.
<Field name="files" component={FileAdapter} />

我从这些地方学到的例子:

我希望能够获取父 react-final-form 表单的文件名(filepond 组件是许多其他字段之一)。就像,我希望它捕捉状态。除此之外,我不确定如何将两者结合起来。

我最终是这样开发的。不幸的是,其他属性没有正确响应,所以我使用 server 属性 来开发解决方案。

const FileAdapter = ({ input: {value, onChange}, meta: { submitError, error } }) => (
  <>
  <FilePond
    acceptedFileTypes={['notRejected'] /* Give fake accepted file type to be able to validate through returning it in fileValidateTypeDetectType once validated it is not one of the rejected file types. */} 
    fileValidateTypeDetectType={(source, type) => new Promise((resolve, reject) => {
      let rejectedExtensions = ['ade', 'adp', 'apk', 'bat', 'chm', 'cmd', 'com', 'cpl', 'dll', 'dmg', 'exe', 'hta', 'ins', 'isp', 'jar', 'js', 'jse', 'lib', 'lnk', 'mde', 'msc', 'msi', 'msp', 'mst', 'nsh', 'pif', 'scr', 'sct', 'shb', 'sys', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh', 'cab'];
      // If the file extension is not in our rejected list.
      if (!rejectedExtensions.some(ext => source.name.endsWith("." + ext))) {
        resolve('notRejected');
      }
      // Otherwise it is rejected.
      resolve(type);
  })}
    name="file"
    allowMultiple
    maxParallelUploads="1"
    id="contact-form-id-file"
    maxTotalFileSize="5MB"
    onremovefile={() => {onChange(value.filter(file => !file.hasOwnProperty('error') ).map(file => file))} /* Remove error messages of rejected file types once you remove on of the erroneous files. */}
    labelIdle="Drag &amp; Drop your files or <span tabindex=&quot;4&quot; class=&quot;filepond--custom-label-action&quot;>Browse</span>."
    server={{
      process: {
        withCredentials: true,
        url: '/api/contact/upload',
        headers: {
          'X-XSRF-TOKEN': axios.defaults.headers.post['X-XSRF-TOKEN'] // Give CSRF token to process header to validate source.
        },
        ondata: (formData) => { // Before sending the files to the server, append size of the current file and the order of the file; order to delete all previous files if the current file is the first, even though the session ID didn't change, and size to validate against total size of files uploaded.
          formData.append('size', formData.getAll('file')[1].size);
          formData.append('quantity', value.length);
          return formData;
        },
        onload: (response) => { // Once response is received, pushed new value to Final Form value variable, and populate through the onChange handler. 
          value.push(JSON.parse(response));
          onChange( value.map(file => file));
          return JSON.parse(response).filename;
        },
        onerror: (response) => { // If error transpires, add error to value with error message.
          value.push({"error": response})
          onChange( value.map(file => file) );
          return response;
        }
      },
      revert: {
        withCredentials: true,
        url: '/api/contact/revert',
        headers: {
          'X-XSRF-TOKEN': axios.defaults.headers.post['X-XSRF-TOKEN'] // Give CSRF token to process header to validate source.
        },
        onload: (response) => { // Update value through onChange once DELETE request sent.
          onChange(value.filter(file => file.filename !== response).map(file => file));
          return response;
        }
      }
    }}
  />
  {(error || submitError) && (<div className="message message--error">{submitError || error}</div>)}
  </>
)

至于最终形式 <Form/> 组件,我是这样结束合并适配器的:

<Form
onSubmit={this.onSubmit}
subscription={{ submitting: true, pristine: true, submitSucceeded: true, submitError: true }} // Optimize performance through subscription.
initialValues={{ files: [] }} // Declare files as array as doing it in FilePond's onInit fires off more than once.
render={({ submitError, handleSubmit, submitting, pristine, submitSucceeded }) => (
    <form className="form-contact text-justify" onSubmit={handleSubmit}>
    <div className="form-contact__fieldset form-contact__fieldset--file fieldset">
        <label htmlFor="contact-form-id-file">File <small className="optional-text">(optional)</small></label>
        <Field name="files" component={FileAdapter} />
    </div>
    {submitError ? <div className="message message--error">{submitError}</div> : submitSucceeded && <div className="message message--success">Thank you for contacting us. We will soon be in touch.</div>}
    <button type="submit" disabled={submitting || pristine || submitSucceeded}>
        {submitting ? <Spinner className="spinner--button form-contact__spinner" fadeIn='quarter' name="line-scale-pulse-out-rapid" /> : submitSucceeded ? 'Message Sent' : 'Send Message'}
    </button>
    </form>
)}
/>

希望这对其他人有帮助。