首页 > 解决方案 > 如何将 FilePond 与 react-final-form 一起使用?

问题描述

我正在尝试让 FilePond 与react-final-form. 我不确定如何将FilePond组件包装在react-final-formField 组件适配器中。

例如,因为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 组件是许多其他字段之一)。如,我希望它捕获状态。除此之外,我不确定如何将两者结合起来。

标签: reactjsreact-final-formfinal-formfilepond

解决方案


我最终像这样开发它。不幸的是,其他属性没有正确响应,所以我使用该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>
)}
/>

希望这对其他人有帮助。


推荐阅读