admin管理员组

文章数量:1312651

I am trying to create custom <input type="file"> upload button with the name of the uploaded file visible on the button itself after the upload, in React. I am creating this as the ponent. I found it very difficult to create a codepen demo so I am just uploading the code here (sorry for that).

import React, { Component, PropTypes } from 'react';
import './InputFile.css';

export default class InputFile extends Component {

constructor(props: any)
{
    super(props);
    this.getUploadedFileName = this.getUploadedFileName.bind(this);
}

getUploadedFileName(selectorFiles: FileList, props) {

const { id } = this.props;

;( function ( document, window, index )
{
    var inputs = document.querySelectorAll(`#${id}`);
    Array.prototype.forEach.call( inputs, function( input )
    {
        var label    = input.nextElementSibling,
            labelVal = label.innerHTML;

        input.addEventListener( 'change', function( e )
        {
            var fileName = '';
            if( this.files && this.files.length > 1 )
                fileName = ( this.getAttribute( 'data-multiple-caption' ) || 
'' ).replace( '{count}', this.files.length );
            else
                fileName = e.target.value.split( '\\' ).pop();

            if( fileName )
                label.querySelector( 'span' ).innerHTML = fileName;
            else
                label.innerHTML = labelVal;
        });

        // Firefox bug fix
        input.addEventListener( 'focus', function(){ input.classList.add( 
'has-focus' ); });
        input.addEventListener( 'blur', function(){ input.classList.remove( 
'has-focus' ); });
    });
}( document, window, 0 ));
}


render () {

    const { id, text, multiple } = this.props;

    return(
        <div>
            <input id={id} type="file" className="km-btn-file" data-multiple-caption="{count} files selected" multiple={multiple} onChange={ (e, id) => this.getUploadedFileName(e.target.files, id)}></input>
            <label htmlFor={id} className="km-button km-button--primary km-btn-file-label">
                <span>{text}</span>
            </label>
        </div>
    );
}
}

InputFile.propTypes = {
    id: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    multiple: PropTypes.string,
};

I am importing this ponent in my other file <InputFile id={'input-file'} text={'Upload File'} multiple={'multiple'}/>

Here is the CSS code

.km-button--primary {
    background-color: #5C5AA7;
    color: #FFFFFF;
}
.km-button {
    border-radius: 3px;
    -webkit-appearance: none;
    border: none;
    outline: none;
    background: transparent;
    height: 36px;
    padding: 0px 16px;
    margin: 0;
    font-size: 14px;
    font-weight: 400;
    text-align: center;
    min-width: 70px;
    transition: all 0.3s ease-out;
}
.km-btn-file {
    width: 0.1px;
      height: 0.1px;
      opacity: 0;
      overflow: hidden;
      position: absolute;
      z-index: -1;
  }
  .km-btn-file-label {
    line-height: 36px;
    cursor: pointer;
  }

The problem I am facing is when I click on the button first time and choose a file to upload it selects the file but does not update the text "Upload File" with the name of the file. But after the click it the second time it works fine. I don't know why that is happening and for that I need help.

Thanks.

I am trying to create custom <input type="file"> upload button with the name of the uploaded file visible on the button itself after the upload, in React. I am creating this as the ponent. I found it very difficult to create a codepen demo so I am just uploading the code here (sorry for that).

import React, { Component, PropTypes } from 'react';
import './InputFile.css';

export default class InputFile extends Component {

constructor(props: any)
{
    super(props);
    this.getUploadedFileName = this.getUploadedFileName.bind(this);
}

getUploadedFileName(selectorFiles: FileList, props) {

const { id } = this.props;

;( function ( document, window, index )
{
    var inputs = document.querySelectorAll(`#${id}`);
    Array.prototype.forEach.call( inputs, function( input )
    {
        var label    = input.nextElementSibling,
            labelVal = label.innerHTML;

        input.addEventListener( 'change', function( e )
        {
            var fileName = '';
            if( this.files && this.files.length > 1 )
                fileName = ( this.getAttribute( 'data-multiple-caption' ) || 
'' ).replace( '{count}', this.files.length );
            else
                fileName = e.target.value.split( '\\' ).pop();

            if( fileName )
                label.querySelector( 'span' ).innerHTML = fileName;
            else
                label.innerHTML = labelVal;
        });

        // Firefox bug fix
        input.addEventListener( 'focus', function(){ input.classList.add( 
'has-focus' ); });
        input.addEventListener( 'blur', function(){ input.classList.remove( 
'has-focus' ); });
    });
}( document, window, 0 ));
}


render () {

    const { id, text, multiple } = this.props;

    return(
        <div>
            <input id={id} type="file" className="km-btn-file" data-multiple-caption="{count} files selected" multiple={multiple} onChange={ (e, id) => this.getUploadedFileName(e.target.files, id)}></input>
            <label htmlFor={id} className="km-button km-button--primary km-btn-file-label">
                <span>{text}</span>
            </label>
        </div>
    );
}
}

InputFile.propTypes = {
    id: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    multiple: PropTypes.string,
};

I am importing this ponent in my other file <InputFile id={'input-file'} text={'Upload File'} multiple={'multiple'}/>

Here is the CSS code

.km-button--primary {
    background-color: #5C5AA7;
    color: #FFFFFF;
}
.km-button {
    border-radius: 3px;
    -webkit-appearance: none;
    border: none;
    outline: none;
    background: transparent;
    height: 36px;
    padding: 0px 16px;
    margin: 0;
    font-size: 14px;
    font-weight: 400;
    text-align: center;
    min-width: 70px;
    transition: all 0.3s ease-out;
}
.km-btn-file {
    width: 0.1px;
      height: 0.1px;
      opacity: 0;
      overflow: hidden;
      position: absolute;
      z-index: -1;
  }
  .km-btn-file-label {
    line-height: 36px;
    cursor: pointer;
  }

The problem I am facing is when I click on the button first time and choose a file to upload it selects the file but does not update the text "Upload File" with the name of the file. But after the click it the second time it works fine. I don't know why that is happening and for that I need help.

Thanks.

Share Improve this question asked Mar 29, 2018 at 12:15 Vibhor SharmaVibhor Sharma 1451 gold badge3 silver badges6 bronze badges 1
  • You should use your lifecycle and state so it updates it directly. – soupette Commented Mar 29, 2018 at 12:47
Add a ment  | 

2 Answers 2

Reset to default 3

You can use the ponent 'state' to update your elements.

constructor(props: any)
{
  super(props);
  this.state = {message:'some initial message'};
}

and for the onChange event do:

getUploadedFileName = (e) => {
   let files = e.target.files,
       value = e.target.value,
       message;
   if( files && files.length > 1 ) message = `${files.length} files selected`;
   else                            message = value.split( '\\' ).pop();

   if(message) this.setState({...this.state,message});
}

and then in the element just bind the value to the state:

<div>
   <input id={id} type="file" className="km-btn-file" 
      data-multiple-caption={this.state.message}
      multiple={multiple} 
      onChange={this.getUploadedFileName}>
   </input>
   <label htmlFor={id} className="km-button km-button--primary km-btn-file-label">
       <span>{text}</span>
   </label>
</div>

You'll need to bind the text property from props to your state, so in your constructor you'll have to do;

this.state = {...props};

or

this.state = { text: props.text, id: props.id, multiple: props.multiple };

Then calling when you want to update the view value instead of manually setting the innerHtml on the label yourself; this.setState({text : new value});

And in your render method;

const { id, text, multiple } = this.state;

What this does is when you call this.setState, it tells React to re-render your ponent which then takes the updated values from the state.

本文标签: javascriptCreating custom Input type file button in ReactStack Overflow