Short explanation of use case: I need to allow the user to include multiple files in a single form, but from different folders (the different folders requirement means I can't simply use the "multiple" attribute.) I'm not allowed to assign File objects to <input>
fields dynamically (source), which means I need to wait until the user chooses a single file, then I need to capture that node (with the file), hide it, and create a new input node in its place.
I have a proof of concept which works with jQuery but I'm trying to figure out how to do this the Redux way. My proposed solution is as follows:
- When the user uploads a file, I dispatch an action which should insert the input DOM node into an array in state and also hide() it.
- Dynamically create a new input node in the exact same place that the first one was
- In another location on the page, display the filenames from the input nodes currently in state
- These filename displays will also have a delete button which would dispatch a delete action which would delete the corresponding node in state.
- When the user submits the form it should include all the file nodes that have been added, and therefore all the files will be submitted at once.
My first question: Is it going to be possible to do this? In particular, can I store a node in state?
My second question: I've not sure how to get a reference to the node to even test this.
Finally, Once I add the node into state and hide() it, how do I go about creating a new <input>
node in its place? As I said, this is easy using jQuery but I'm interested in how to do it the "redux" way
Here's some code I have
Component - Uploader.js
import React, { Component } from 'react';
class Uploader extends Component {
renderHiddenInputs(files){
if(!this.props.files || this.props.files.length == 0) {
return
}
return files.map((file) => {
return (
<li>
<h3>{file}</h3> // return file node
</li>
)
});
}
renderFileNames(files){
if(!this.props.files || this.props.files.length == 0) {
return <h3>There are no files</h3>
}
return files.map((file) => {
return (
<li>
<h3>{file.files[0].name}</h3> //return filenames only
</li>
)
});
}
render() {
return (
<div>
<div><input type="file" id="fileinput" onChange={this.props.addInput}/></div> //How do I pass in a ref to this exact node so It can be store in state and then create a new one in it's place?
<hr />
<div>{this.renderHiddenInputs(this.props.files)}</div> //we need the input nodes on the page so when the form submits the files get sent
<div>
<ul>
{this.renderFileNames(this.props.files)} //the input nodes are hidden so here we display the filenames for the user to see
</ul>
</div>
</div>
);
}
}
Container - UploaderContainer.js
import { connect } from 'react-redux'
import { uploadFile, addInput } from '../actions/index';
import Uploader from '../components/Uploader';
const mapStateToProps = (state) => {
return {
files: state.files
};
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
addInput: (inputNode) => { //How do I get inputNode?
dispatch(addInput(inputNode))
}
}
}
const UploaderContainer = connect(mapStateToProps, mapDispatchToProps)(Uploader)
export default UploaderContainer