Meteor-files: Do you have sample code for Meteor with React?

Created on 10 Aug 2016  路  14Comments  路  Source: veliovgroup/Meteor-Files

Any code showing the use in Meteor 1.3 with React, jsx files?

Documentation help wanted

Most helpful comment

Here are my react components (two of them, put the individualfile in a separate file to make it cleaner). One is for the upload portion and one displays each file. I have methods for renaming and deleting on the server where you should do lots of validation.. Also has a progress bar for uploading.

I wasn't able to get this to work with the web workers enabled, but I saw some recent work in that area so it might be fixed, so set that to true if you need it.

This is using the older mixin method for meteor data. I also added console logs so you can put different things in the node events and see whats going on.

Thanks for the awesome work btw!

import React from 'react';
import {Meteor} from 'meteor/meteor';
import IndividualFile from './FIleIndividualFile.jsx';
import {_} from 'meteor/underscore';

const FileUploadComponent = React.createClass({
    mixins: [ReactMeteorData],

    getInitialState(){
        return {
            uploading: [],
            progress: 0,
            inProgress: false
        }
    },

    getMeteorData() {
        var handle = Meteor.subscribe('files.all');
        return {
            docsReadyYet: handle.ready(),
            docs: UserFiles.find().fetch()
        };
    },

    uploadIt(e){
        "use strict";
        e.preventDefault();

        let self = this;

        if (e.currentTarget.files && e.currentTarget.files[0]) {
            // We upload only one file, in case
            // there was multiple files selected
            var file = e.currentTarget.files[0];

            if (file) {
                let uploadInstance = UserFiles.insert({
                    file: file,
                    meta: {
                        locator: self.props.fileLocator,
                        userId: Meteor.userId()
                    },
                    streams: 'dynamic',
                    chunkSize: 'dynamic',
                    allowWebWorkers: false
                }, false);

                self.setState({
                    uploading: uploadInstance,
                    inProgress: true
                });

                uploadInstance.on('start', function () {
                    console.log('Starting');
                });

                uploadInstance.on('end', function (error, fileObj) {
                    console.log('On end File Object: ', fileObj);
                });

                uploadInstance.on('uploaded', function (error, fileObj) {
                    console.log('uploaded: ', fileObj);

                   //remove the filename from the upload box
                    self.refs['fileinput'].value = '';

                    self.setState({
                        uploading: [],
                        progress: 0,
                        inProgress: false
                    });
                });

                uploadInstance.on('error', function (error, fileObj) {
                    console.log('Error during upload: ' + error);
                });

                uploadInstance.on('progress', function (progress, fileObj) {
                    console.log('Upload Percentage: ' + progress);
                    self.setState({
                        progress: progress
                    })
                });

                uploadInstance.start();
            }
        }
    },

    showUploads() {

        console.log('**********************************', this.state.uploading);

        if (!_.isEmpty(this.state.uploading)) {

            return <div>
                {this.state.uploading.file.name}
                <div className="progress progress-bar-default">
                    <div style={{width: this.state.progress + '%'}} aria-valuemax="100"
                         aria-valuemin="0"
                         aria-valuenow={this.state.progress || 0} role="progressbar"
                         className="progress-bar">
                        <span className="sr-only">{this.state.progress}% Complete (success)</span>
                        <span>{this.state.progress}%</span>
                    </div>
                </div>
            </div>
        }
    },


    render() {

        if (this.data.docsReadyYet) {
            'use strict';

            let fileCursors = this.data.docs;

            let showit = fileCursors.map((aFile, key) => {
                // console.log('A file: ', aFile.link(), aFile.get('name'));

                let link = UserFiles.findOne({_id: aFile._id}).link();

                return <div key={'file' + key}>
                    <IndividualFile
                        fileName={aFile.name}
                        fileUrl={link}
                        fileId={aFile._id}
                        fileSize={aFile.size}
                    />
                </div>
            });

            return <div>
                <div className="row">
                    <div className="col-md-12">
                        <p>Upload New File:</p>
                        <input type="file" id="fileinput" disabled={this.state.inProgress} ref="fileinput"
                               onChange={this.uploadIt}/>
                    </div>
                </div>

                <div className="row m-t-sm m-b-sm">
                    <div className="col-md-6">

                        {this.showUploads()}

                    </div>
                    <div className="col-md-6">
                    </div>
                </div>

                {showit}

            </div>
        }
        else return <div></div>
    }
});


const IndividualFile = React.createClass({

    propTypes: {
        fileName: React.PropTypes.string.isRequired,
        fileSize: React.PropTypes.number.isRequired,
        fileUrl: React.PropTypes.string,
        fileId: React.PropTypes.string.isRequired
    },


    removeFile(){
        "use strict";
        let conf = confirm('Are you sure you want to delete the file?') || false;
        if (conf == true) {
            Meteor.call('RemoveFile', this.props.fileId, function (err, res) {
                if (err)
                    console.log(err);
            });
        }
    },


    renameFile(){
        "use strict";

        let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi;

        let prompt = window.prompt('New file name?', this.props.fileName);

        //replace any non valid characters
        if (prompt) {
            prompt = prompt.replace(validName, '-');
            prompt.trim();
        }

        if (!_.isEmpty(prompt)) {
            Meteor.call('RenameFile', this.props.fileId, prompt, function (err, res) {
                if (err)
                    console.log(err);
            });
        }
    },

    render() {

        return <div className="m-t-sm">

            <div className="row">
                <div className="col-md-12">
                    <strong>{this.props.fileName}</strong>
                    <div className="m-b-sm">
                    </div>
                </div>

            </div>

            <div className="row">
                <div className="col-md-3">
                    <button onClick={this.renameFile} className="btn btn-outline btn-primary btn-sm">
                        Rename
                    </button>
                </div>


                <div className="col-md-3">
                    <a href={this.props.fileUrl} className="btn btn-outline btn-primary btn-sm"
                       target="_blank">View</a>
                </div>

                <div className="col-md-2">
                    <button onClick={this.removeFile} className="btn btn-outline btn-danger btn-sm">
                        Delete
                    </button>
                </div>

                <div className="col-md-4">
                    Size: {this.props.fileSize}
                </div>
            </div>
        </div>
    }
});

export default FileUploadComponent;

All 14 comments

No samples for React.
But I can answer on more specific questions.

BTW PRs is always welcome

@maitrid You can try this one. First put the < template > code on your main.html file then try this in your component.jsx:

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze'; 

export default class ComponentName extends Component {

    componentDidMount() {
      // Use Meteor Blaze to render
      this.view = Blaze.render(Template.uploadForm,
        ReactDOM.findDOMNode(this.refs.uploadContainer));
    }

    componentWillUnmount() {
      // Clean up Blaze view
      Blaze.remove(this.view);
    }

    render() {
    ...
    }
}


Template.uploadForm.onCreated(function () {
  this.currentUpload = new ReactiveVar(false);
});

Template.uploadForm.helpers({
  currentUpload: function () {
    return Template.instance().currentUpload.get();
  }
});

Template.uploadForm.events({
//....
});

I admit this is not the best code sample for React but let me know if this works for you :)

Is this code should be considered as docs/wiki candidate?

Well it's based from Meteor's docs about using Blaze templates in React. I have tested it and it seems to be working fine. I think you can consider it but I really hope someone else would come and improve this code :)

Got it, will set appropriate tags/labels

Thank you, dsmalicsi! I'll try it. 2 days ago, i took a different route, using the npm package react-dropzone, and then some custom script to handle the upload. i've yet to figure out result handling and progress bar. If this works, that would be great!

Here are my react components (two of them, put the individualfile in a separate file to make it cleaner). One is for the upload portion and one displays each file. I have methods for renaming and deleting on the server where you should do lots of validation.. Also has a progress bar for uploading.

I wasn't able to get this to work with the web workers enabled, but I saw some recent work in that area so it might be fixed, so set that to true if you need it.

This is using the older mixin method for meteor data. I also added console logs so you can put different things in the node events and see whats going on.

Thanks for the awesome work btw!

import React from 'react';
import {Meteor} from 'meteor/meteor';
import IndividualFile from './FIleIndividualFile.jsx';
import {_} from 'meteor/underscore';

const FileUploadComponent = React.createClass({
    mixins: [ReactMeteorData],

    getInitialState(){
        return {
            uploading: [],
            progress: 0,
            inProgress: false
        }
    },

    getMeteorData() {
        var handle = Meteor.subscribe('files.all');
        return {
            docsReadyYet: handle.ready(),
            docs: UserFiles.find().fetch()
        };
    },

    uploadIt(e){
        "use strict";
        e.preventDefault();

        let self = this;

        if (e.currentTarget.files && e.currentTarget.files[0]) {
            // We upload only one file, in case
            // there was multiple files selected
            var file = e.currentTarget.files[0];

            if (file) {
                let uploadInstance = UserFiles.insert({
                    file: file,
                    meta: {
                        locator: self.props.fileLocator,
                        userId: Meteor.userId()
                    },
                    streams: 'dynamic',
                    chunkSize: 'dynamic',
                    allowWebWorkers: false
                }, false);

                self.setState({
                    uploading: uploadInstance,
                    inProgress: true
                });

                uploadInstance.on('start', function () {
                    console.log('Starting');
                });

                uploadInstance.on('end', function (error, fileObj) {
                    console.log('On end File Object: ', fileObj);
                });

                uploadInstance.on('uploaded', function (error, fileObj) {
                    console.log('uploaded: ', fileObj);

                   //remove the filename from the upload box
                    self.refs['fileinput'].value = '';

                    self.setState({
                        uploading: [],
                        progress: 0,
                        inProgress: false
                    });
                });

                uploadInstance.on('error', function (error, fileObj) {
                    console.log('Error during upload: ' + error);
                });

                uploadInstance.on('progress', function (progress, fileObj) {
                    console.log('Upload Percentage: ' + progress);
                    self.setState({
                        progress: progress
                    })
                });

                uploadInstance.start();
            }
        }
    },

    showUploads() {

        console.log('**********************************', this.state.uploading);

        if (!_.isEmpty(this.state.uploading)) {

            return <div>
                {this.state.uploading.file.name}
                <div className="progress progress-bar-default">
                    <div style={{width: this.state.progress + '%'}} aria-valuemax="100"
                         aria-valuemin="0"
                         aria-valuenow={this.state.progress || 0} role="progressbar"
                         className="progress-bar">
                        <span className="sr-only">{this.state.progress}% Complete (success)</span>
                        <span>{this.state.progress}%</span>
                    </div>
                </div>
            </div>
        }
    },


    render() {

        if (this.data.docsReadyYet) {
            'use strict';

            let fileCursors = this.data.docs;

            let showit = fileCursors.map((aFile, key) => {
                // console.log('A file: ', aFile.link(), aFile.get('name'));

                let link = UserFiles.findOne({_id: aFile._id}).link();

                return <div key={'file' + key}>
                    <IndividualFile
                        fileName={aFile.name}
                        fileUrl={link}
                        fileId={aFile._id}
                        fileSize={aFile.size}
                    />
                </div>
            });

            return <div>
                <div className="row">
                    <div className="col-md-12">
                        <p>Upload New File:</p>
                        <input type="file" id="fileinput" disabled={this.state.inProgress} ref="fileinput"
                               onChange={this.uploadIt}/>
                    </div>
                </div>

                <div className="row m-t-sm m-b-sm">
                    <div className="col-md-6">

                        {this.showUploads()}

                    </div>
                    <div className="col-md-6">
                    </div>
                </div>

                {showit}

            </div>
        }
        else return <div></div>
    }
});


const IndividualFile = React.createClass({

    propTypes: {
        fileName: React.PropTypes.string.isRequired,
        fileSize: React.PropTypes.number.isRequired,
        fileUrl: React.PropTypes.string,
        fileId: React.PropTypes.string.isRequired
    },


    removeFile(){
        "use strict";
        let conf = confirm('Are you sure you want to delete the file?') || false;
        if (conf == true) {
            Meteor.call('RemoveFile', this.props.fileId, function (err, res) {
                if (err)
                    console.log(err);
            });
        }
    },


    renameFile(){
        "use strict";

        let validName = /[^a-zA-Z0-9 \.:\+()\-_%!&]/gi;

        let prompt = window.prompt('New file name?', this.props.fileName);

        //replace any non valid characters
        if (prompt) {
            prompt = prompt.replace(validName, '-');
            prompt.trim();
        }

        if (!_.isEmpty(prompt)) {
            Meteor.call('RenameFile', this.props.fileId, prompt, function (err, res) {
                if (err)
                    console.log(err);
            });
        }
    },

    render() {

        return <div className="m-t-sm">

            <div className="row">
                <div className="col-md-12">
                    <strong>{this.props.fileName}</strong>
                    <div className="m-b-sm">
                    </div>
                </div>

            </div>

            <div className="row">
                <div className="col-md-3">
                    <button onClick={this.renameFile} className="btn btn-outline btn-primary btn-sm">
                        Rename
                    </button>
                </div>


                <div className="col-md-3">
                    <a href={this.props.fileUrl} className="btn btn-outline btn-primary btn-sm"
                       target="_blank">View</a>
                </div>

                <div className="col-md-2">
                    <button onClick={this.removeFile} className="btn btn-outline btn-danger btn-sm">
                        Delete
                    </button>
                </div>

                <div className="col-md-4">
                    Size: {this.props.fileSize}
                </div>
            </div>
        </div>
    }
});

export default FileUploadComponent;

Hi React users,

Could please any of you take the beast from this thread and send PR to docs? Or at least point me to the right direction.

Sincerely.

@dr-dimitru Sure I can add it, give me a day to add some comments, etc..

@vtocco thank you

@vtocco great, could you send a PR to dev branch, just add this as docs/react-example.md file.
Feel free to add link to docs/toc.md and readme.md

Done, React Integration Example, thanks to @vtocco

@vtocco , I'm waiting for your PR and links in ToC and Readme

@vtocco thank you for great contribution! All docs is merged, updated and published.

Btw, anyone in this thread is tried/interested in WebRTC support for file uploads? Please, take a look on this discussion: https://forums.meteor.com/t/meteor-files-filescollection-webrtc-dc-for-upload/28262

Was this page helpful?
0 / 5 - 0 ratings