Reputation: 349
I am new to React and I'm implementing a multi-file upload system. I current have implemented user driven file selection via a click event handler on a button which adds the selected file into an array.
For some reason when ever I select a file, the following error is shown in the console which causes the application to stop working:
Uncaught Invariant Violation: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead
For reference, my interface for this functionality looks like:
And here are the relevant parts of my component implementation:
state = {
selectedPortifolio: {},
portflioFiles: []
}
And this method is called on the file onchange event
loadPortfolio = (e) => {
if (e.target.files) {
this.setState({
selectedPortifolio: {
file: e.target.files[0],
filename: e.target.files[0].name
}
});
}
};
And when add button is clicked then this method is called.
addPortfolio = () => {
if (
this.state.selectedPortifolio &&
this.state.selectedPortifolio.file
) {
let items = this.state.portflioFiles;
items.push(this.state.selectedPortifolio);
this.setState({ selectedPortifolio: {}, portflioFiles: items });
console.log(this.state.portflioFiles);
}
};
After all this i load this array into the div
{this.state.portflioFiles.length > 0 ? (
this.state.portflioFiles.map((item, index) => {
return (
<div className="portfolio-file" key={index}>
{getDataURL(item.file).then((data) => {
return (
<image
src={data}
alt={item.filename}
/>
);
})}
</div>
);
})
) : (
<div></fdv>
)}
And getDataURL method is defined in another file.
export const getDataURL = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
Upvotes: 2
Views: 266
Reputation: 30360
Currently, your code is attempting to directly render promise objects by mapping portfolioItems
via the call to getDataURL()
function. At the moment, React is based on synchronous rendering which means "promise-based rendering" is not currently possbile with React.
To achieve what you require, consider resolving the promises returned by getDataURL()
outside of your rendering logic (ie in addPortfolio()
).
This would require that your component state is extended to hold the image data resolved by the promise returned by getDataURL()
. You'd then update the rendering logic of your component so that images a rendered from image data now present in your component state:
Update state shape
state = {
selectedPortifolio: {},
portflioFiles: [],
portflioImages : [] // Extend state with arrage for image data
}
Update addPortfolio()
addPortfolio = () => {
if (
this.state.selectedPortifolio &&
this.state.selectedPortifolio.file
) {
let items = this.state.portflioFiles;
let images = this.state.portflioImages;
items.push(this.state.selectedPortifolio);
// Resolve promise outside of render-logic. Once data
// required for rendering is available, update state
// to trigger a re-render
getDataURL(this.state.selectedPortifolio).then((image) => {
this.setState({
selectedPortifolio: {},
portflioFiles: items,
// Add resolved image to portflioImages images array
portflioImages : images.concat([image])
});
});
}
};
Update render logic
{this.state.portflioImages.length > 0 ? (
/* map portflioImages directly to image without promise */
this.state.portflioImages.map((imageSrc, index) => {
return (
<div className="portfolio-file" key={index}>
<img src={imageSrc} alt={item.filename} />
</div>
);
})
) : (
<div></fdv>
)}
Hope that helps!
Upvotes: 1