61

I've been looking everywhere for some help on making a component to help manage uploading files from within React to an endpoint I have setup.

I've tried numerous options, including integrating filedropjs. I decided against it because I don't have control over the elements it sets up in the DOM with the new FileDrop('zone', options);

This is what I have so far:

module.exports =  React.createClass({
displayName: "Upload",
handleChange: function(e){

    formData = this.refs.uploadForm.getDOMNode();

    jQuery.ajax({
        url: 'http://example.com',
        type : 'POST',
        xhr: function(){
            var myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){
                myXhr.upload.addEventListener('progress',progressHandlingFunction, false);
            }
            return myXhr;
        },
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        success: function(data){
            alert(data);
        }
    });

},
render: function(){

        return (
            <form ref="uploadForm" className="uploader" encType="multipart/form-data" onChange={this.handleChange}>
                <input ref="file" type="file" name="file" className="upload-file"/>
            </form>
        );
   }

 });



},
render: function(){

    console.log(this.props.content);

    if(this.props.content != ""){
        return (
            <img src={this.props.content} />
        );
    } else {
        return (
            <form className="uploader" encType="multipart/form-data" onChange={this.handleChange}>
                <input ref="file" type="file" name="file" className="upload-file"/>
            </form>
        );
    }
}
});

If someone could just point me in the right direction I would send some virtual hugs. I've been working on this quite extensively. I feel like I'm close, but not quite there.

Thanks!

4
  • Not sure if you can use this or not -- possibly not -- but just in case: hayageek.com/docs/jquery-upload-file.php Commented Feb 28, 2015 at 20:56
  • how about something as simple as this jsbin.com/qulozo/2 ? Commented Feb 28, 2015 at 21:17
  • I put together a simple blog post that outlines simple steps to take to upload an image using React with Node.js and also upload to S3: benrlodge.com/blog/post/… Commented Dec 19, 2015 at 5:43
  • @Dan Can you post the solution you ended up choosing? Thank you Commented Jun 6, 2016 at 19:44

4 Answers 4

68

I worked on this quite a while as well. This what I came up with.

A Dropzone component, coupled with using superagent.

// based on https://github.com/paramaggarwal/react-dropzone, adds image preview    
const React = require('react');
const _ = require('lodash');

var Dropzone = React.createClass({
  getInitialState: function() {
    return {
      isDragActive: false
    }
  },

  propTypes: {
    onDrop: React.PropTypes.func.isRequired,
    size: React.PropTypes.number,
    style: React.PropTypes.object
  },

  onDragLeave: function(e) {
    this.setState({
      isDragActive: false
    });
  },

  onDragOver: function(e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';

    this.setState({
      isDragActive: true
    });
  },

  onDrop: function(e) {
    e.preventDefault();

    this.setState({
      isDragActive: false
    });

    var files;
    if (e.dataTransfer) {
      files = e.dataTransfer.files;
    } else if (e.target) {
      files = e.target.files;
    }

    _.each(files, this._createPreview);
  },

  onClick: function () {
    this.refs.fileInput.getDOMNode().click();
  },

  _createPreview: function(file){
    var self = this
      , newFile
      , reader = new FileReader();

    reader.onloadend = function(e){
      newFile = {file:file, imageUrl:e.target.result};
      if (self.props.onDrop) {
        self.props.onDrop(newFile);
      }
    };

    reader.readAsDataURL(file);
  },

  render: function() {

    var className = 'dropzone';
    if (this.state.isDragActive) {
      className += ' active';
    };

    var style = {
      width: this.props.size || 100,
      height: this.props.size || 100,
      borderStyle: this.state.isDragActive ? 'solid' : 'dashed'
    };

    return (
      <div className={className} onClick={this.onClick} onDragLeave={this.onDragLeave} onDragOver={this.onDragOver} onDrop={this.onDrop}>
        <input style={{display: 'none' }} type='file' multiple ref='fileInput' onChange={this.onDrop} />
        {this.props.children}
      </div>
    );
  }

});

module.exports = Dropzone

Using the Dropzone.

    <Dropzone onDrop={this.onAddFile}>
      <p>Drag &amp; drop files here or click here to browse for files.</p>
    </Dropzone>

When a file is added to the drop zone, add it to your list of files to upload. I add it to my flux store.

  onAddFile: function(res){
    var newFile = {
      id:uuid(),
      name:res.file.name,
      size: res.file.size,
      altText:'',
      caption: '',
      file:res.file,
      url:res.imageUrl
    };
    this.executeAction(newImageAction, newFile);
  }

You can use the imageUrl to display a preview of the file.

  <img ref="img" src={this.state.imageUrl} width="120" height="120"/>

To upload the files, get the list of files and send them through superagent. I'm using flux, so I get the list of images from that store.

  request = require('superagent-bluebird-promise')
  Promise = require('bluebird')

    upload: function(){
      var images = this.getStore(ProductsStore).getNewImages();
      var csrf = this.getStore(ApplicationStore).token;
      var url = '/images/upload';
      var requests = [];
      var promise;
      var self = this;
      _.each(images, function(img){

        if(!img.name || img.name.length == 0) return;

        promise = request
          .post(url)
          .field('name', img.name)
          .field('altText', img.altText)
          .field('caption', img.caption)
          .field('size', img.size)
          .attach('image', img.file, img.file.name)
          .set('Accept', 'application/json')
          .set('x-csrf-token', csrf)
          .on('progress', function(e) {
            console.log('Percentage done: ', e.percent);
          })
          .promise()
          .then(function(res){
            var newImg = res.body.result;
            newImg.id = img.id;
            self.executeAction(savedNewImageAction, newImg);
          })
          .catch(function(err){
            self.executeAction(savedNewImageErrorAction, err.res.body.errors);
          });
        requests.push(promise);
      });

      Promise
        .all(requests)
        .then(function(){
          console.log('all done');
        })
        .catch(function(){
          console.log('done with errors');
        });
    }
Sign up to request clarification or add additional context in comments.

5 Comments

What is this.executeAction(newImageAction, newFile); doing? Shouldn't newImageAction be replaced by something? Also, where should the final code block go - in the Dropzone component or the Store?
@frogbandit this.executeAction() is from the Fluxible framework by Yahoo. fluxible.io - what final code block are you referring to?
@JoeMcBride Gotcha. What is it supposed to do though? It's always giving me newImageAction is not defined
The codeblock I'm referring to (my section question) is everything from request = require('superagent-bluebird-promise') down.
@frogbandit The second codeblock is example code to upload a list of images to a server using superagent. newImageAction is a flux action. You don't have to use executeAction unless you are using fluxible. You can use the object created, newFile, and do whatever you want with it.
28

This may help

var FormUpload = React.createClass({
    uploadFile: function (e) {
        var fd = new FormData();    
        fd.append('file', this.refs.file.getDOMNode().files[0]);

        $.ajax({
            url: 'http://localhost:51218/api/Values/UploadFile',
            data: fd,
            processData: false,
            contentType: false,
            type: 'POST',
            success: function(data){
                alert(data);
            } 
        });
        e.preventDefault()
    },
    render: function() {
        return (
            <div>                
               <form ref="uploadForm" className="uploader" encType="multipart/form-data" >
                   <input ref="file" type="file" name="file" className="upload-file"/>
                   <input type="button" ref="button" value="Upload" onClick={this.uploadFile} />
               </form>                
            </div>
        );
    }
});

borrowed from here How to send FormData objects with Ajax-requests in jQuery?

2 Comments

This is aweseome! Works perfectly across all browsers i could think of. Thanks!
As of React 0.14 "this.refs.file" is the DOM node, so getDOMNode is deprecated. The fd.append line becomes: <code>fd.append('file', this.refs.file.files[0]);</code>
2

I was faced with the task of getting that facebook or gmail-like behavior where your drop target highlights as soon as the user begins dragging a file anywhere over the window. There was no off-the-shelf React drag and drop solution that I could find. So, I made one.

It is meant to be bare-bones, supplying you with a base to customize and style as your own. It provides many hooks to enable you to do this. But there is also a demo that gives you an example to go off of.

Check it out: https://www.npmjs.com/package/react-file-drop

Demo: http://sarink.github.io/react-file-drop/demo/

2 Comments

@KabirSarin react-file-drop has file remove feature ?
@KabirSarin Also is it possible to customize the file type I want to accept ?
2

There is a Dropzone npm package for this https://www.npmjs.com/package/react-dropzone

1 Comment

okonet.ru/react-dropzone accepts only images ? I need file drag and drop with specifed type. is it possible ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.