Hey! awesome work with redux-form!
I'm using it at production, and I'm going to use it for file inputs, but I would appreciate an example or ideia of how it would be done with redux-form.
thanks!
As far as I know, this is how:
1) It's important to note that <input type="file" />
Doesn't support the setting of values, so if you want to bind the values back to something you'll need to write a custom "selected files" component.
2) You need to do some parsing of the event before calling fields.yourField.handleChange()
. Something like this:
<input
type="file"
onChange={
( e ) => {
e.preventDefault();
const { fields } = this.props;
// convert files to an array
const files = [ ...e.target.files ];
fields.yourField.handleChange(files);
}
}
/>
3) Do your validation in the normal way
Hope that helps.
@erikras: What do you think about updating getValue
to support e.target.files? https://github.com/erikras/redux-form/blob/master/src/reduxForm.js#L28
@BBB thanks for the help! I finally did it using the https://github.com/paramaggarwal/react-dropzone
@erikras great patch! I'll use it in later integrations with other components ;)
I have got this working in all current browsers, but IE 9 just return undefined, perhaps for older browsers you could return the field value?
@lemonCMS You mean like changing this:
if (type === 'file') {
return files || dataTransfer && dataTransfer.files;
}
to this:
if (type === 'file') {
return files || dataTransfer && dataTransfer.files || value;
}
??
Yes :)
I'm kinda struggling getting this to work. I'm under the impression I'm using the file input wrongly.
My initial attempt was to bind the same way I would bind any other input:
<input type='file' {...image} />
But as @BBB points out, React complains that value
cannot be set programatically. Second try; handle the change manually (after @BBB's suggestion):
handleFile(fieldName, event) {
event.preventDefault();
const { fields } = this.props;
// convert files to an array
const files = [ ...event.target.files ];
fields[fieldName].handleChange(files);
},
render() {
(...)
<input type='file' onChange={this.handleFile.bind(this, 'image')} />
(...)
}
But handleChange
is not defined on the field. Am I completely misunderstanding the suggestion, or has the API changed since the suggestion was made?
I am not sure, but i think i had it working as following.
const props = this.props.fields[field.name];
return (
<input
type="file"
onDrop={props.onDrop}
onChange={props.onChange}
onFocus={props.onFocus}
onUpdate={props.onUpdate}
/>
);
And then the filelist will be send to your onSubmit function.
Yes, what @lemonCMS said should work. Although it could just be <input type="file" {...field}/>
. :smile:
I can't guarantee onDrop
will work. Drag and drop works with text between fields. I have not tested with a file input.
Tried @lemonCMS' approach; changed
fields[fieldName].handleChange(files);
to
fields[fieldName].onChange(files);
which works.
The <input type="file" {...field} />
approach definitely throws an error!
Yes. handleChange
went extinct in v3.0
. :+1:
@edorivai, @lemonCMS, @sebas5384: Do you guys have any examples of your approach to using file fields with redux-form? Especially if you were able to get it to work with a Dropzone component.
@duro Haven't tried myself, but I think the following would work:
<Dropzone onDrop={this.props.fields.myFileField.onChange}>
<div>Try dropping some files here, or click to select files to upload.</div>
</Dropzone>
+1 does anyone have an example of redux-form + dropzone?
@austinmao have you found an example maybe? :)
Nope :(
@duro @austinmao @kitze I've put together a simple example of using dropzone with redux-form. http://ollie.relph.me/blog/redux-form-and-dropzone-react-example/ Any questions, just post an issue on the repo!
Did anyone had an experience with asyncValidate and Dropzone?
I'm trying to upload a file using react-dropzone and async validate a successful/unsuccessful file upload. The problem is i can't get asyncBlurFields to work with react-dropzone, and calling asyncValidate directly inside onChange doesn't work because 'values' object in asyncValidate is outdated and don't contain the file i want to upload. Any ideas?
Thanks!
I don't know if this helps anybody but this approach: <input type="file" {...field} />
didn't threw an error when I specified the value={null}
inside of it like so:
<input type="file" {...profilePic} value={null} />
@serranoarevalo That is, in effect, converting it back to an uncontrolled input, which might be necessary for file inputs, I don't know. The React docs are light on that corner of the DOM.
where is the binary content of fole i can save for example to rethinkDB using r.binary ? (if i am using react-dropzone in redux) there is not any field that holds the data of file.. ?
Hello, is there a simple way to use Dropzone with the new api Field
, I'm struggling with the onChange
binding :/ As we cannot access this.props.fields
anymore. Using a ref ?
Thank you,
Hello everybody.
For interested people, I managed to make it work with:
<Field name="picture" component={props =>
<Dropzone
{...props.input}
multiple={false}
style={dropzoneStyle}
onDrop={(filesToUpload) => {
this.files = filesToUpload;
return props.input.onChange(filesToUpload);
}}
>
<div>Try dropping a file here, or click to select file to upload.</div>
</Dropzone>
} type="file"/>
{this.files &&
<div>
{this.files.map((file, i) => <span key={i}>{file.name}</span>)}
</div>
}
For information files
is an array declared in my component as : files: []
.
If you don't need to display current selected files, you can just use onDrop = props.input.onChange
Documentation should handle this behavior.
Hope it will helps ;)
@BBB's earliest comment here works for me (on [email protected] and [email protected]), save for one issue: the first time we select a file, the store is updated with the new form value and the component is rendered with the appropriate value; in subsequent changes to the file input, the store is updated properly and the component is rerendered but always receives the first file we chose.
I've uploaded a minimal example to http://carpeliam.github.io/redux-form-file-input-example, with code at https://github.com/carpeliam/redux-form-file-input-example.
@erikras not sure if you saw the above comment - should I file a separate issue? Not sure if 5.3.1 is supposed to work with React 15.1 or not.
Hi I was trying to use dropzone with redux-form
export class Dropzone extends React.Component {
render() {
const { id, field, label, labelSm, controlSm, ...other } = this.props;
return (
<div>
<FormGroup
controlId={id}
validationState={validationState(field.meta)}
>
<Col componentClass={ControlLabel} sm={labelSm}>
{label}
</Col>
<Col sm={controlSm}>
<ReactDropzone
{...field.input}
{...other}
onDrop={field.input.onChange}
>
<div>Try dropping a file here, or click to select file to upload.</div>
</ReactDropzone>
<FormControl.Feedback />
{errorMsg(field.meta)}
</Col>
</FormGroup>
{field.input.value && <div>{field.input.value.map((file, i) => <span key={i}>{file.name}</span>)}</div>}
</div>
);
}
}
where ReactDropzone = require('react-dropzone')
and using it like this in one of the forms that I have
<Field
name={'files'}
component={(field) =>
<Dropzone
id={'files'}
label={'Documents'}
field={field}
/>
}
/>
And I get a state mutation
Uncaught Invariant Violation: A state mutation was detected between dispatches, in the path `form.abc.values.files.0.lastModifiedDate`. This may cause incorrect behavior.
where abc is the form name.
Can anyone point out where the problem is?
How does the new <Field>
look with files type? Do I need my own file component? I can't seem to do type="file"
https://github.com/BBB/dropzone-redux-form-example Has now been updated to work with v6
. @shawnmclean @jckdrpr You might find it helpful.
Hey @BBB I tried out the example on the repo linked in your answer and I added redux-immutable-state-invariant
to the project and I am getting the same error. Has no one else faced this issue?
Well it does work when there is no redux-immutable-state-invariant
.
My take
import React from 'react'
class FileInput extends React.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
return (<input
type="file"
value={value}
onChange={this.onChange}
/>)
}
}
export default FileInput
and then
import { Field } from 'redux-form'
<Field
type="file"
name="poster"
component={FileInput}
/>
Works awesome!
thanks to @BBB i came up with the following
import React from 'react';
import Dropzone from 'react-dropzone';
const ReduxFormDropzone = (field) => {
let {
input,
meta,
dropzoneOnDrop,
...props
} = field;
return (
<Dropzone
onDrop={(acceptedFiles, rejectedFiles, e) => {
field.input.onChange(acceptedFiles);
field.dropzoneOnDrop && field.dropzoneOnDrop(acceptedFiles, rejectedFiles, e);
}}
{...props}
/>
);
}
export default ReduxFormDropzone;
then you just need to do...
<Field
name={"files"}
component={ReduxFormDropzone}
style={style.dropzone}
multiple={false}
dropzoneOnDrop={this.handleDrop}
/>
@BBB I am getting the same error as @jckdrpr . Please suggest some solution for same.
browser.js?9520:40 Uncaught Error: A state mutation was detected between dispatches, in the path
form.SkillForm.values.files.0.lastModifiedDate. This may cause incorrect behavior.
@jckdrpr Have you find any solution for above mention error?
@BBB your example returns empty formdata and the server cant find the payload. Any idea?
@asiniy did not work with v6 throw this error:
Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
at Object.updateWrapper (webpack:///../~/react-dom/lib/ReactDOMInput.js?:156:20)
at ReactDOMComponent.forceUpdateIfMounted (webpack:///../~/react-dom/lib/ReactDOMInput.js?:34:19)
at CallbackQueue.notifyAll (webpack:///../~/react-dom/lib/CallbackQueue.js?:76:22)
at Object.flushBatchedUpdates (webpack:///../~/react-dom/lib/ReactUpdates.js?:180:13)
at ReactDefaultBatchingStrategyTransaction.closeAll (webpack:///../~/react-dom/lib/Transaction.js?:206:25)
at ReactDefaultBatchingStrategyTransaction.perform (webpack:///../~/react-dom/lib/Transaction.js?:153:16)
at Object.batchedUpdates (webpack:///../~/react-dom/lib/ReactDefaultBatchingStrategy.js?:62:26)
at Object.batchedUpdates (webpack:///../~/react-dom/lib/ReactUpdates.js?:97:27)
at dispatchEvent (webpack:///../~/react-dom/lib/ReactEventListener.js?:147:20)
My solution based on https://github.com/erikras/redux-form/issues/2532#issuecomment-277926328. I don't know how hacky is it, but works for me:
import React from 'react'
export const FileInput = ({ input, resetKey }) => {
const { value, ...inputProps } = input
const handleChange = (e) => {
input.onChange(e.target.files[0])
}
return (
<input {...inputProps} key={resetKey} type="file" onChange={handleChange} onBlur={() => {}} />
)
}
<Field name="image" component={FileInput} onChange={this.handleFileChange} />
I'm saving data URI from image cropper as value of the field. The problem was that onBlur was resetting field value to FileList object, so I had to disable it.
What worked in my case:
<input type="file"/>
component.value
property from field.input
in the functional component<Field component={} />
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
/**
* File input workarround:
* More info: http://redux-form.com/5.2.5/#/examples/file?_k=57hmlw
*/
const customFileInput = (field) => {
delete field.input.value; // <-- just delete the value property
return <input type="file" id="file" {...field.input} />;
};
class SubmitVideoForm extends Component {
render() {
return (
<form className="submit-video-form">
<Field
name="file"
type="file"
component={customFileInput}/>
<input type="submit" value="Submit your video"/>
</form>
);
}
}
SubmitVideoForm = reduxForm({
form: 'submitVideoForm'
})(SubmitVideoForm);
export default SubmitVideoForm;
You can test this by querying the file input field's FileList length:
document.querySelector("#file").files.length;
Just one thing to point in the answer from @asiniy, just remove the value attribute form the returned input to avoid error "Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string."
return (<input
type="file"
onChange={this.onChange}
/>)
More information http://redux-form.com/5.2.5/#/examples/file?_k=z12d2f
Hi. I have same problem like https://github.com/erikras/redux-form/issues/71#issuecomment-243444038
Is there a way to solve the problem?
I'm having the same problem as @sherubthakur mentioned with redux-immutable-state-invariant
. Any solutions?
About the problem from https://github.com/erikras/redux-form/issues/71#issuecomment-243444038 https://github.com/erikras/redux-form/issues/71#issuecomment-239203867, I found out that deep-equal
used in this condition, returns true (equal) in the second part of the condition, given two different instances of File object.
A work around for https://github.com/erikras/redux-form/issues/71#issuecomment-239203867 is to not return the File object as value to the onChange
of the field, but to return an object like:
{
file: e.target.files[0],
name: e.target.files[0].name,
}
As long as the name is different, the value will be updated in the store as well as the validation will be performed. As a fix I suggest to reimplement the deep-equal function.
@SepiaGroup You are the man!
Do you manage to do the image preview as well?
@pacozaa glad you found it helpful.
preview should work, what seems to be the issue?
@SepiaGroup
Thank you again!
I forgot they are array value because it assume everything there are multiple files.
So I solve my problem just add [0]
behind variable!
Anyone who face the same problem please don't forget to add [0]
like this
file[0]
Again use @SepiaGroup trick works like a charm.
@LulzAugusto I have the same issues as you. Did you ever find a solution?
@lupitadavila Oh sorry for such delay in answering you (damn github notifications are such mess). About your question, I didn't find a solution, but I've updated redux-immutable-state-invariant
and I'm now using their ignore
parameter to pretend that the issue doesn't exist anymore. 馃槀
@GuillaumeCisco hey, I have tried your method mentioned at here https://github.com/erikras/redux-form/issues/71#issuecomment-234234555 it seems work but may I know how to get the url from it? Because the results returned it is always show as [object FileList] as shown in the screenshot below:
I just want to get the url from it, thank you
Hey @JohnHour89 , please understand how FileList works https://developer.mozilla.org/en-US/docs/Web/API/FileList
It is just an array of objects.
If you want to create an url from a File object, you can use data
or blob
https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
Hope it will help
I have a form which using onSubmit
of the Form
:
<Form onSubmit={handleSubmit(this.onSubmit)}>
The function onSubmit can only capture the string, number... but not the file
onSubmit(formData) {
}
The examples above using onChange
method to capture the file, but I want to use onSubmit
, any suggestion?
Have problem with save in store, file from input[type="file"]
馃槗
I am using Fields Array (<FieldArray name="companyCertificates" component={InputFile} />
) to generate new inputs after adding file. After input onChange I trying to save e.target.files[0]
to store through input.onChange(e.target.files[0])
, but Redux Inspector is showing me empty object.
Maybe I use not correct method to save or Redux Inspector don't show file value?
This my InputFile
component:
import React, { Component } from 'react';
import { Field } from 'redux-form';
const InputField = ({ input, fields, index }) => {
delete input.value;
const fieldAdd = (e, fields) => {
if (e.target.files[0]) {
fields.push();
input.onChange(e.target.files[0])
}
};
return (
<input type="file"
{...input}
onChange={(e) => {
e.preventDefault();
fieldAdd(e, fields, input);
}}
accept=".pdf" />
)
};
export default class InputFile extends Component {
constructor (props) {
super(props);
};
componentWillMount () {
let { fields } = this.props;
if (fields.length === 0)
fields.insert(0, null);
};
render () {
let { fields } = this.props;
return (
<div className="input-group input-file">
<div className="files">
{fields.map((name, index) => {
return (
<Field name={name}
key={index}
index={index}
fields={fields}
component={InputField} />
)
})}
</div>
</div>
)
}
};
Could smbdy help me with saving this file in store to send it later?
Thanx a lot!
@tini2n the problem is in this part
// should be a Class
const fieldAdd = (e, fields) => {
if (e.target.files[0]) {
// you are pushing nothing
fields.push();
// input is unknown
input.onChange(e.target.files[0])
}
};
return (
<input type="file"
{...input}
onChange={(e) => {
e.preventDefault();
// passing input parameter but not using it
fieldAdd(e, fields, input);
}}
accept=".pdf" />
)
@GuillaumeCisco thanx for reply!)
fields.push()
is FieldsArray's method to add new input elements. Seems that it working well.
input
is known as input element. Below console.log of it
I checked Diff of values and saw that value in store is replaced by empty object:
@tini2n Your code is too messy. Please clean it, use a Class for InputField
and declare correctly your functions and parameters.
There is no declaration of FieldArray in your sample code.
Furthermore, you are pushing nothing, see the doc about FieldArray's push method:
fields.push(value:Any) : Function
Adds a value to the end of the array. Returns nothing.
This is not a mutator; it dispatches an action which updates the state in Redux, which will cause your component to rerender.
@tini2n Did you ever get a field array to work with file type inputs?
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
My take
and then
Works awesome!