I have used react dropzone component for multiple image upload but the image preview is not shown on my template.Files are shown on console.Where have i done wrong? I have used this component from here https://github.com/okonet/react-dropzone . let me know what else i have to provide to examine this issue with fine-tooth comb. Thanks for help in advance. Your help will be appreciated.
The code is
var Gallery = React.createClass({
getInitialState: function () {
return {
files:[]
};
},
onDrop(files) {
console.log('Received files: ', files);
this.setState({
files: files
});
},
showFiles() {
var files = this.state;
console.log('files',files);
if (!files.length) {
return null;
}
return (
<div>
<h3>Dropped files: </h3>
<ul className="col-md-4">
{
files.map((file, idx) => {
return (
<li key={idx}>
<img src={file.preview} width={100}/>
<div>{file.name + ' : ' + file.size + ' bytes.'}</div>
</li>
);
})
}
</ul>
</div>
);
},
render: function () {
return (
<div>
<div className="col-md-12">
<Dropzone onDrop={this.onDrop}>
Try dropping some files here, or click to select files to upload.
</Dropzone>
{this.showFiles()}
</div>
<div className="row">
<button className="btn" onClick={this.submit}>Next</button>
</div>
</div>
);
},
});
The problem is in the showFiles method.
var files = this.state;
//const {files}=this.state
you don't assign the files to the file var, but the whole state.
i managed to solve the problem by doing this
var files = this.state.files || null;
Related
I'm trying to log the height and the width values of an image to the console in react.js but they are (for some reason) undefined. Do you know why it is?
Here's the code:
export default function FilmPage(props) {
const getImgSize = (img) => {
let imageEl = <img onLoad={()=>{
console.log(imageEl.width)
console.log(imageEl.height)
}} src={img} />
return imageEl
}
return (
<div className="proper-film">
<div className="body">
<div>{getImgSize(props.data.image)}</div>
</div>
</div>
)
}
And also the data I retrieve looks like this:
data = {
some: null,
other: null,
stuff: null,
image: "https://my_api.com/images/original/picture.jpg",
and_more: null
}
By the way, when I tried debugging, there was no problem with the data I retrieve.
What would you recommend?
You can modify getImgSize method like below
const getImgSize = (img) => {
return <img onLoad={(e) => {
console.log(e.target.offsetHeight)
console.log(e.target.offsetWidth)
}} src={img} />
}
https://codesandbox.io/s/xenodochial-borg-x474m?file=/src/App.js
I'm developing a WP Gutenberg block based on https://github.com/JimSchofield/Guty-Blocks-2 and I'm running into an issue where the saved content doesn't match the editor when loaded therefore I'm seeing an error 'This block contains unexpected or invalid content'.
I have tried looking in the browser console but I can't figure out where the discrepancy is, both the edit and save functions reference the images but they're not being stored by the save function.
It's worth noting that once the block is loaded for the first time, used and the post is saved it works correctly on the front-end. It's when you go back to the editor it doesn't work anymore.
import './__block__.view.scss';
import './__block__.editor.scss';
const {
registerBlockType,
getBlockDefaultClassName
} = wp.blocks;
const {
InspectorControls,
MediaUpload
} = wp.editor;
const {
Button
} = wp.components;
registerBlockType('__namespace__/__block__', {
title: '__prettyname__(noCase)',
icon: '__icon__',
category: '__category__',
attributes: {
imgUrl: {
type: 'array',
source: 'children',
selector: 'img',
},
},
edit({ attributes, className, setAttributes }) {
//Destructuring the images array attribute
const {images = []} = attributes;
// This removes an image from the gallery
const removeImage = (removeImage) => {
//filter the images
const newImages = images.filter( (image) => {
//If the current image is equal to removeImage the image will be returnd
if(image.id != removeImage.id) {
return image;
}
});
//Saves the new state
setAttributes({
images:newImages,
})
}
//Displays the images
const displayImages = (images) => {
return (
//Loops throug the images
images.map( (image) => {
return (
<div className="gallery-item-container">
<img className='gallery-item' src={image.url} key={ images.id } />
<div className='remove-item' onClick={() => removeImage(image)}><span class="dashicons dashicons-trash"></span></div>
<div className='caption-text'>{image.caption[0]}</div>
</div>
)
})
)
}
//JSX to return
return (
<div>
<MediaUpload
onSelect={(media) => {setAttributes({images: [...images, ...media]});}}
type="image"
multiple={true}
value={images}
render={({open}) => (
<Button className="select-images-button is-button is-default is-large" onClick={open}>
Add images
</Button>
)}
/>
<br />
<div class="modal__img">
<div class="flexslider">
<ul class="slides" data-total-slides={images.length}>{ displayImages(images) }</ul>
</div>
</div>
</div>
);
},
save({attributes}) {
// Destructuring the images array attribute
const { images = [] } = attributes;
// Displays the images
const displayImages = (images) => {
return (
images.map( (image,index) => {
return (
<li><img
className='lazy'
key={images.id}
data-src={image.url}
data-slide-no={index}
data-caption={image.caption[0]}
alt={image.alt}
/></li>
)
})
)
}
//JSX to return
return (
<div class="modal__img">
<div class="flexslider">
<ul class="slides" data-total-slides={images.length}>{ displayImages(images) }</ul>
</div>
</div>
);
},
});
I expected the block to output the original HTML when back in the editor, but this behaviour does not work.
In both the save and edit function your are referencing images from the attributes prop. Yet, when you register your block and set up the attributes, you only have imageUrl as an attribute. This means images are never getting stored in the DB, and do not exist when you come back to edit.
Adding images as a attribute should fix this.
What you have
attributes: {
imgUrl: {
type: 'array',
source: 'children',
selector: 'img',
},
},
What it should be
attributes: {
images: {
type: 'array',
default: []
},
},
Try passing props instead of attributes in edit and save functions, and then simply use
var attributes = props.attributes;
For more reference read the code in these examples.
I am working on React JS app, where I am trying to upload files to Firebase. I can easily upload the files and even a single file but the problem is when I am trying to show the progress of the file uploaded on individual file. I have built a UI, when a user selects files, the files get rendered and shows the file size, name and type etc. When I click the upload button, the upload get started in the background. What I am trying to do is, how can I show the progress of multiple files on the View.
The State
this.state = {
selectedFilesPrev: [],
filesPreviewAble: false,
multiple: true,
showLoaders: false
}
The JSX DOM
render() {
if(!this.state.filesPreviewAble){
return (
<div className='container'>
<div className='file-uploader-page' >
<div className='upload-icon'> <img src={uploadIcon} alt='upload'/> </div>
<p> Drag and Drop Files </p>
<p className='orSeparate'>Or</p>
<div className='files-upload-btn'>
<input type='file' id='file-upload' multiple={this.state.multiple} onChange={this.buttonUpload}/>
<label htmlFor='file-upload' className='rcaBtn'> Upload Files </label>
</div>
</div>
</div>
)
} else if(this.state.filesPreviewAble){
let l = '';
if(this.state.showLoaders){
l = <div className='file-uploading-bar'> <div className='fub-uploaded' style={{width:`${this.state.selectedFilesPrev.progress.percent}%`}}></div> </div>
} else { l = ''}
return (
<div className='container'>
<div className='files-preview container-fluid' >
<div className='fp-head'> Files to be Uploaded </div>
<div className='fp-files'>
{
this.state.selectedFilesPrev.map( (e, i) => {
return (
<div className='single-file row' key={i}>
<div className='file-preview col-2'>
<img src={e.img} alt='icon' />
</div>
<div className='file-meta col-9'>
<span className='file-name'> {e.meta.name} </span>
<span className='file-size'> {this.formatFileSize(e.meta.size)} - {e.meta.type} </span>
</div>
<div className='file-close col-1'> <span onClick={this.removeFile}>×</span> </div>
{l}
</div>
)
})
}
</div>
<div className='fp-upload'>
<button className='rcaBtn' onClick={this.handleUploadingFiles}>Upload Now</button>
</div>
</div>
</div>
)
}
}
The Files Selector Code
buttonUpload(event){
let object = event.target.files;
let files = []
for (const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
files.push(element)
}
}
let i = 0;
files.map( f => {
this.generatePreviewData(f, (prev) => {
i++;
this.state.selectedFilesPrev.push({
meta : f,
img : prev,
progress : {
percent : 0,
upoaded : false
}
})
if(i === files.length){
this.setState({
filesPreviewAble : true
})
}
})
return 0;
})
}
The Uploader Function (Which Works Perfect)
handleUploadingFiles(){
console.log(this.state.selectedFilesPrev)
this.setState({ showLoaders : true })
this.state.selectedFilesPrev.map( (file, idx) => {
let task = stdb.ref(`Images/${file.meta.name}`).put(file.meta);
task.on('state_changed', snapshot => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
this.state.selectedFilesPrev[idx].progress.percent = progress;
console.log("FILE # "+idx,' Upload is ' + progress + '% done');
},
error => {
console.log(error)
},
() => {
stdb.ref('Images').child(file.meta.name).getDownloadURL().then( url => {
console.log(url)
})
})
})
}
Note : What I want is simply, when the user click the upload button, each and every file uploading progress should be shown on the UI. The handleUploadingFiles() have the progress, which outputs the progress for each task, but I am unable to show that progress on the component.
I have this dropZone that is clickable and opens up the file selecting thing. And once a file is selected from your computer it gives a preview of it (if it's an image). I would like to be able to drag and drop the same file and it do the same thing as if I were to click in the div and open up a file.
I tried calling my handleChange function inside of the onDrop but it didn't quite work and I would get an TypeError: Cannot read property 'target' of undefined error or it just wouldn't do anything at all. Here is a CodeSandbox of the dropzone component on it's own.
Here's that snippet of my code:
class DropZone extends Component {
constructor(props){
super(props)
this.state = {
file: "",
fileId: uuid(),
className: 'dropZone'
}
this.handleChange = this.handleChange.bind(this)
this._onDragEnter = this._onDragEnter.bind(this);
this._onDragLeave = this._onDragLeave.bind(this);
this._onDragOver = this._onDragOver.bind(this);
this._onDrop = this._onDrop.bind(this);
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
console.log("handleChange")
//document.getElementsByClassName("dropZone").style.backgroundImage = 'url(' + this.state.file + ')';
}
componentDidMount() {
window.addEventListener('mouseup', this._onDragLeave);
window.addEventListener('dragenter', this._onDragEnter);
window.addEventListener('dragover', this._onDragOver);
document.getElementById('dragbox').addEventListener('dragleave', this._onDragLeave);
window.addEventListener('drop', this._onDrop);
}
componentWillUnmount() {
window.removeEventListener('mouseup', this._onDragLeave);
window.removeEventListener('dragenter', this._onDragEnter);
window.addEventListener('dragover', this._onDragOver);
document.getElementById('dragbox').removeEventListener('dragleave', this._onDragLeave);
window.removeEventListener('drop', this._onDrop);
}
_onDragEnter(e) {
e.stopPropagation();
e.preventDefault();
return false;
}
_onDragOver(e) {
e.preventDefault();
e.stopPropagation();
return false;
}
_onDragLeave(e) {
e.stopPropagation();
e.preventDefault();
return false;
}
_onDrop(e, event) {
e.preventDefault();
this.handleChange
let files = e.dataTransfer.files;
console.log('Files dropped: ', files);
// Upload files
//this.handleChange.bind(this)
console.log(this.state.file)
return false;
}
render() {
const uniqueId = this.state.fileId;
return (
<div>
<input type="file" id={uniqueId} name={uniqueId} class="inputFile" onChange={this.handleChange}/>
<label htmlFor={uniqueId} value={this.state.file}>
{this.props.children}
<div className="dropZone" id="dragbox" onChange={this.handleChange}>
Drop or Choose File
<img src={this.state.file} id="pic" name="file" accept="image/*"/>
</div>
</label>
<div>
</div>
</div>
);
}
}
There's going to be multiple of these that upload their own individual photo/file. Thanks!
You needed to pass on the e parameter to handleChange() and correctly grab the files from the event.
onDrop
_onDrop(e, event) {
e.preventDefault();
this.handleChange(e.dataTransfer.files[0]);
let files = e.dataTransfer.files;
console.log("Files dropped: ", files);
// Upload files
console.log(this.state.file);
return false;
}
handleChange
handleChange(file = "") {
this.setState({
file: URL.createObjectURL(file)
});
//document.getElementsByClassName("dropZone").style.backgroundImage = 'url(' + this.state.file + ')';
}
And in render do parsing of the event to get the file:
onChange={e => this.handleChange(e.target.files[0])}
Alternatively, you could make two handleChange to clean mess up your render method.
Link to working example: https://codesandbox.io/s/7k6y94k6w6 (based on your code)
I saw some questions speaking about similar issues but somehow I still do not manage to solve my issue so here I am asking for your kind help. I am pretty new to React and would like to send a function from a Parent to a child and then use it from the Child but somehow when I want to use it it says
Uncaught TypeError: Cannot read property 'props' of undefined"
Edited Code after first answers were helping:
var Menu = React.createClass({
links : [
{key : 1, name : "help", click : this.props.changePageHelp}
],
render : function() {
var menuItem = this.links.map(function(link){
return (
<li key={link.key} className="menu-help menu-link" onClick={link.click}>{link.name}</li>
)
});
return (
<ul>
{menuItem}
</ul>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function() {
console.log('help');
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
Pass a value from Menu function and recieve it in the changePageHelp function and it works.
var Menu = React.createClass({
render : function() {
return (
<div>
{this.props.changePageHelp('Hello')}
</div>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function(help) {
return help;
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="admin"></div>
For performance reasons, you should avoid using bind or arrow functions in JSX props. This is because a copy of the event handling function is created for every instance generated by the map() function. This is explained here: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
To avoid this you can pull the repeated section into its own component. Here is a demo: http://codepen.io/PiotrBerebecki/pen/EgvjmZ The console.log() call in your parent component receives now the name of the link. You could use it for example in React Router.
var Admin = React.createClass ({
_changePageHelp : function(name) {
console.log(name);
},
render : function () {
return (
<div>
<div id="menu-admin">
<Menu changePageHelp={this._changePageHelp} />
</div>
</div>
);
}
});
var Menu = React.createClass({
getDefaultProps: function() {
return {
links: [
{key: 1, name: 'help'},
{key: 2, name: 'about'},
{key: 3, name: 'contact'}
]
};
},
render: function() {
var menuItem = this.props.links.map((link) => {
return (
<MenuItem key={link.key}
name={link.name}
changePageHelp={this.props.changePageHelp}
className="menu-help menu-link" />
);
});
return (
<ul>
{menuItem}
</ul>
);
}
});
var MenuItem = React.createClass ({
handleClick: function() {
this.props.changePageHelp(this.props.name);
},
render : function () {
return (
<li onClick={this.handleClick}>
Click me to console log in Admin component <b>{this.props.name}</b>
</li>
);
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));