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

时间:2019-04-20 18:47:25

标签: reactjs react-final-form final-form filepond

我正在尝试使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组件是许多其他字段之一)。在这种情况下,我希望它捕获状态。除此之外,我不确定如何将两者整合。

1 个答案:

答案 0 :(得分:1)

我最终像这样开发了它。不幸的是,其他属性无法正确响应,因此我使用了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>
)}
/>

希望这对其他人有帮助。