如何使用redux表单上传文件?

如何使用redux表单上传文件?,redux,redux-form,Redux,Redux Form,尝试上载文件时,无法将正确的值输入存储区。我得到的不是文件内容,而是类似于{0:{}。 代码如下: const renderInput = field => ( <div> <input {...field.input} type={field.type}/> { field.meta.touched && field.meta.error && <span classNa

尝试上载文件时,无法将正确的值输入存储区。我得到的不是文件内容,而是类似于
{0:{}
。 代码如下:

const renderInput = field => (
  <div>
    <input {...field.input} type={field.type}/>
    {
      field.meta.touched &&
      field.meta.error &&
      <span className={styles.error}>{field.meta.error}</span>
    }
  </div>
);

render() {

  ...

  <form className={styles.form} onSubmit={handleSubmit(submit)}>
    <div className={styles.interface}>
      <label>userpic</label>
      <Field
        name="userpic"
        component={renderInput}
        type="file"
      />
    </div>
    <div>
      <button type="submit" disabled={submitting}>Submit</button>
    <div>
  </form>

  ...

}
const renderInput=field=>(
{
field.meta.touch&&
field.meta.error&&
{field.meta.error}
}
);
render(){
...
用户权限
提交
...
}
我在web上找到的所有示例都是使用redux表单的v5制作的


如何在redux form v6中输入文件?

我的redux form输入包装器示例

import React,{Component,PropTypes}来自'React';
从“react Dropzone”导入Dropzone;
从“元素”导入{Form};
从'redux form'导入{Field};
类FileInput扩展组件{
静态类型={
dropzone_选项:PropTypes.object,
meta:PropTypes.object,
标签:PropTypes.string,
classNameLabel:PropTypes.string,
输入:PropTypes.object,
类名:PropTypes.string,
子项:PropTypes.node,
cbFunction:PropTypes.func,
};
静态defaultProps={
类名:“”,
cbFunction:()=>{},
};
render(){
const{className,input:{onChange},dropzone_选项,meta:{error,toucted},label,classnamelab,children,name,cbFunction}=this.props;
返回(
{label&&p className={classnamelab | |'''}>{label}

} { CBF功能; 一经更改即返回(f); }} className=“dropzone输入” name={name} > {儿童} {错误&&T?错误:“” ); } } 导出默认道具=>;
热使用它:

<FileInput
 name="add_photo"
 label="Others:"
  classNameLabel="file-input-label"
  className="file-input"
  dropzone_options={{
    multiple: false,
    accept: 'image/*'
  }}
>
  <span>Add more</span>
</FileInput>

添加更多

呈现预览图像的另一种方法(下面的示例使用React 16+语法,只接受一个图像文件发送到API;但是,通过一些小的调整,它还可以缩放到多个图像和其他输入字段):

工作示例

工作示例(过时)


之前

之后


容器/UploadForm.js

import React, { Component } from "react";
import { Form, Field, reduxForm } from "redux-form";
import DropZoneField from "../components/dropzoneField";

const imageIsRequired = value => (!value ? "Required" : undefined);

class UploadImageForm extends Component {
  state = { imageFile: [] };

  handleFormSubmit = formProps => {
    const fd = new FormData();
    fd.append("imageFile", formProps.imageToUpload.file);
    // append any additional Redux form fields
    // create an AJAX request here with the created formData

    alert(JSON.stringify(formProps, null, 4));
  };

  handleOnDrop = (newImageFile, onChange) => {
    const imageFile = {
      file: newImageFile[0],
      name: newImageFile[0].name,
      preview: URL.createObjectURL(newImageFile[0]),
      size: newImageFile[0].size
    };

    this.setState({ imageFile: [imageFile] }, () => onChange(imageFile));
  };

  resetForm = () => this.setState({ imageFile: [] }, () => this.props.reset());

  render = () => (
    <div className="app-container">
      <h1 className="title">Upload An Image</h1>
      <hr />
      <Form onSubmit={this.props.handleSubmit(this.handleFormSubmit)}>
        <Field
          name="imageToUpload"
          component={DropZoneField}
          type="file"
          imagefile={this.state.imageFile}
          handleOnDrop={this.handleOnDrop}
          validate={[imageIsRequired]}
        />
        <button
          type="submit"
          className="uk-button uk-button-primary uk-button-large"
          disabled={this.props.submitting}
        >
          Submit
        </button>
        <button
          type="button"
          className="uk-button uk-button-default uk-button-large"
          disabled={this.props.pristine || this.props.submitting}
          onClick={this.resetForm}
          style={{ float: "right" }}
        >
          Clear
        </button>
      </Form>
      <div className="clear" />
    </div>
  );
}

export default reduxForm({ form: "UploadImageForm" })(UploadImageForm);
import React from "react";
import PropTypes from "prop-types";
import DropZone from "react-dropzone";
import ImagePreview from "./imagePreview";
import Placeholder from "./placeholder";
import ShowError from "./showError";

const DropZoneField = ({
  handleOnDrop,
  input: { onChange },
  imagefile,
  meta: { error, touched }
}) => (
  <div className="preview-container">
    <DropZone
      accept="image/jpeg, image/png, image/gif, image/bmp"
      className="upload-container"
      onDrop={file => handleOnDrop(file, onChange)}
    >
      {({ getRootProps, getInputProps }) =>
        imagefile && imagefile.length > 0 ? (
          <ImagePreview imagefile={imagefile} />
        ) : (
          <Placeholder
            error={error}
            touched={touched}
            getInputProps={getInputProps}
            getRootProps={getRootProps}
          />
        )
      }
    </DropZone>
    <ShowError error={error} touched={touched} />
  </div>
);

DropZoneField.propTypes = {
  error: PropTypes.string,
  handleOnDrop: PropTypes.func.isRequired,
  imagefile: PropTypes.arrayOf(
    PropTypes.shape({
      file: PropTypes.file,
      name: PropTypes.string,
      preview: PropTypes.string,
      size: PropTypes.number
    })
  ),
  label: PropTypes.string,
  onChange: PropTypes.func,
  touched: PropTypes.bool
};

export default DropZoneField;
import React from "react";
import PropTypes from "prop-types";

const ImagePreview = ({ imagefile }) =>
  imagefile.map(({ name, preview, size }) => (
    <div key={name} className="render-preview">
      <div className="image-container">
        <img src={preview} alt={name} />
      </div>
      <div className="details">
        {name} - {(size / 1024000).toFixed(2)}MB
      </div>
    </div>
  ));

ImagePreview.propTypes = {
  imagefile: PropTypes.arrayOf(
    PropTypes.shape({
      file: PropTypes.file,
      name: PropTypes.string,
      preview: PropTypes.string,
      size: PropTypes.number
    })
  )
};

export default ImagePreview;
import React from "react";
import PropTypes from "prop-types";
import { MdCloudUpload } from "react-icons/md";

const Placeholder = ({ getInputProps, getRootProps, error, touched }) => (
  <div
    {...getRootProps()}
    className={`placeholder-preview ${error && touched ? "has-error" : ""}`}
  >
    <input {...getInputProps()} />
    <MdCloudUpload style={{ fontSize: 100, paddingTop: 85 }} />
    <p>Click or drag image file to this area to upload.</p>
  </div>
);

Placeholder.propTypes = {
  error: PropTypes.string,
  getInputProps: PropTypes.func.isRequired,
  getRootProps: PropTypes.func.isRequired,
  touched: PropTypes.bool
};

export default Placeholder;
import React from "react";
import PropTypes from "prop-types";
import { MdInfoOutline } from "react-icons/md";

const ShowError = ({ error, touched }) =>
  touched && error ? (
    <div className="error">
      <MdInfoOutline
        style={{ position: "relative", top: -2, marginRight: 2 }}
      />
      {error}
    </div>
  ) : null;

ShowError.propTypes = {
  error: PropTypes.string,
  touched: PropTypes.bool
};

export default ShowError;
img {
  max-height: 240px;
  margin: 0 auto;
}

.app-container {
  width: 500px;
  margin: 30px auto;
}

.clear {
  clear: both;
}

.details,
.title {
  text-align: center;
}

.error {
  margin-top: 4px;
  color: red;
}

.has-error {
  border: 1px dotted red;
}

.image-container {
  align-items: center;
  display: flex;
  width: 85%;
  height: 80%;
  float: left;
  margin: 15px 10px 10px 37px;
  text-align: center;
}

.preview-container {
  height: 335px;
  width: 100%;
  margin-bottom: 40px;
}

.placeholder-preview,
.render-preview {
  text-align: center;
  background-color: #efebeb;
  height: 100%;
  width: 100%;
  border-radius: 5px;
}

.upload-container {
  cursor: pointer;
  height: 300px;
}
import React from 'react'
import { useDropzone } from 'react-dropzone'
function MyDropzone(props) {

    const onDrop = (filesToUpload) => {
        return props.input.onChange(filesToUpload[0]);
    }

    const onChange = (filesToUpload) => {
        return props.input.onChange(filesToUpload[0]);
    }

    const { getRootProps, getInputProps } = useDropzone({ onDrop });
    return (
         <div {...getRootProps()}>
          <input {...getInputProps()} onChange={e => onChange(e.target.files)} />
          <p> Drop or select yout file</p>
         </div>
        )
}

export default MyDropzone;
     <Field
       name="myfile"
       component={renderFile}
     />

创建一个字段组件,如:

import React, {Component} from 'react'

export default class FieldFileInput  extends Component{
  constructor(props) {
    super(props)
    this.onChange = this.onChange.bind(this)
  }

  onChange(e) {
    const { input: { onChange } } = this.props
    onChange(e.target.files[0])
  }

  render(){
    const { input: { value } } = this.props
    const {input,label, required, meta, } = this.props  //whatever props you send to the component from redux-form Field
    return(
     <div><label>{label}</label>
     <div>
       <input
        type='file'
        accept='.jpg, .png, .jpeg'
        onChange={this.onChange}
       />
     </div>
     </div>
    )
}
}
import React,{Component}来自“React”
导出默认类FieldFileInput扩展组件{
建造师(道具){
超级(道具)
this.onChange=this.onChange.bind(this)
}
onChange(e){
const{input:{onChange}}=this.props
onChange(e.target.files[0])
}
render(){
const{input:{value}}=this.props
const{input,label,required,meta,}=this.props//从redux表单字段发送到组件的任何props
返回(
{label}
)
}
}

将此组件传递到所需的字段组件。如果您需要一个简单的文件上传功能,则无需额外的Dropzone或其他库

如果您需要base64编码将其发送到后端,以下是一个适用于我的修改版本:

export class FileInput extends React.Component {

  getBase64 = (file) => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
    });
  }

  onFileChange = async (e) => {
    const { input } = this.props
    const targetFile = e.target.files[0]
    if (targetFile) {
      const val = await this.getBase64(targetFile)
      input.onChange(val)
    } else {
      input.onChange(null)
    }
  }

  render() {

    return (
      <input
        type="file"
        onChange={this.onFileChange}
      />
    )
  }
}
导出类FileInput扩展React.Component{
getBase64=(文件)=>{
返回新承诺((解决、拒绝)=>{
const reader=new FileReader();
reader.readAsDataURL(文件);
reader.onload=()=>resolve(reader.result);
reader.onerror=错误=>拒绝(错误);
});
}
onFileChange=async(e)=>{
const{input}=this.props
const targetFile=e.target.files[0]
如果(目标文件){
const val=wait this.getBase64(targetFile)
input.onChange(val)
}否则{
input.onChange(null)
}
}
render(){
返回(
)
}
}
然后,字段组件将如下所示:


我成功地在材质ui包装文本字段上使用redux表单,如下所示:

B4编辑:

编辑后:

 <Field name="image" component={FileTextField} floatingLabelText={messages.chooseImage} fullWidth={true} />


组件定义为:

const styles = {
  button: {
    margin: 12
  },
  exampleImageInput: {
    cursor: 'pointer',
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    width: '100%',
    opacity: 0
  },
  FFS:{
    position: 'absolute',
    lineHeight: '1.5',
    top: '38',
    transition: 'none',
    zIndex: '1',
    transform: 'none',
    transformOrigin: 'none',
    pointerEvents: 'none',
    userSelect: 'none',
    fontSize: '16',
    color: 'rgba(0, 0, 0, 0.8)',
  }
};

export const FileTextField  = ({
                                  floatingLabelText,
                                  fullWidth,
                                  input,
                                  label,
                                  meta: { touched, error },
                                  ...custom })=>{
  if (input.value && input.value[0] && input.value[0].name) {
    floatingLabelText = input.value[0].name;
  }
  delete input.value;
  return (
    <TextField
      hintText={label}
      fullWidth={fullWidth}
      floatingLabelShrinkStyle={styles.FFS}
      floatingLabelText={floatingLabelText}
      inputStyle={styles.exampleImageInput}
      type="file"
      errorText={error}
      {...input}
      {...custom}
    />
  )
}
const styles={
按钮:{
差额:12
},
示例图像输入:{
光标:“指针”,
位置:'绝对',
排名:0,
底部:0,
右:0,,
左:0,,
宽度:“100%”,
不透明度:0
},
自由流速度:{
位置:'绝对',
线宽:“1.5”,
前:"38",,
过渡:“无”,
zIndex:'1',
转换:“无”,
transformOrigin:“无”,
pointerEvents:'无',
用户选择:“无”,
字体大小:“16”,
颜色:“rgba(0,0,0,0.8)”,
}
};
导出常量FileTextField=({
浮动标签文本,
全宽,
输入,
标签,
meta:{触摸,错误},
…自定义})=>{
if(input.value&&input.value[0]&&input.value[0].name){
floatingLabelText=input.value[0]。名称;
}
删除输入值;
返回(
)
}

表示React>=16和ReduxForm>=8(测试版本分别为React的16.8.6和8.2.5) 工作如下组件

(解决方案由DarkBitz发布在相关网站上)

constadaptefileeventtovalue=delegate=>e=>delegate(e.target.files[0]);
常量文件输入=({
输入:{value:ommitvalue,onChange,onBlur,…inputProps},
梅塔:梅塔,
…道具
}) => {
返回(
);
};
导出常量文件上传=(道具)=>{
const{handleSubmit}=props;
const onFormSubmit=(数据)=>{
控制台日志(数据);
}
返回(
附件
提交
)
}

带Redux表单

const{handleSubmit}=props;
//制作一个常量文件来保存文件道具。
const file=useRef();
//创建一个函数,将redux表单输入文件值替换为自定义值。
constfileupload=()=>{
//jsx获取文件输入
//更改时存储文件/fil
import React from 'react'
import { useDropzone } from 'react-dropzone'
function MyDropzone(props) {

    const onDrop = (filesToUpload) => {
        return props.input.onChange(filesToUpload[0]);
    }

    const onChange = (filesToUpload) => {
        return props.input.onChange(filesToUpload[0]);
    }

    const { getRootProps, getInputProps } = useDropzone({ onDrop });
    return (
         <div {...getRootProps()}>
          <input {...getInputProps()} onChange={e => onChange(e.target.files)} />
          <p> Drop or select yout file</p>
         </div>
        )
}

export default MyDropzone;
     <Field
       name="myfile"
       component={renderFile}
     />