27

I have a INPUT BUTTON and INPUT FILE, I want to click the BUTTON and it will trigger the INPUT FILE event in REACT JS.

React.createElement('input',{type:'file', name:'myfile'})

then the button

React.createElement('a',{onClick: this.doClick},'Select File')

So how to define and trigger the INPUT FILE click event when we click the A HREF?

Your help is appreciate. :-)

Yarin Nim
  • 3,607
  • 3
  • 14
  • 15

9 Answers9

57

Update: Sep 18, 2021

Note: On NextJS, I was facing onChange event is not trigged from input file element. For that, we can use onInputCapture or onChangeCapture. For more detailed information, Stackoverflow - onChange event is not firing

Basic example on onChangeCapture as per our requirement. Requires React ^16.8,

const Dummy = () => {
  const inputFileRef = React.useRef();
  const onFileChangeCapture = ( e: React.ChangeEvent<HTMLInputElement> ) {
    /*Selected files data can be collected here.*/
    console.log(e.target.files);
  };
  const onBtnClick = () => {
    /*Collecting node-element and performing click*/
    inputFileRef.current.click();
  };
  return (
    <form>
      <input
        type="file"
        ref={inputFileRef}
        onChangeCapture={onFileChangeCapture}
      />
      <button onClick={onBtnClick}>Select file</button>
    </form>
  );
};

Using useRef Hook in functional components. Requires React ^16.8,

const Dummy = () => {
  const inputFileRef = useRef( null );

  const onFilechange = ( e ) => {
    /*Selected files data can be collected here.*/
    console.log( e.target.files );
  }
  const onBtnClick = () => {
    /*Collecting node-element and performing click*/
    inputFileRef.current.click();
  }

  return (
    <form className="some-container">
      <input
        type="file"
        ref={inputFileRef}
        onChange={onFileChange}
      />
      <button onClick={onBtnClick}>Select file</button>
    </form>
  )
}

Class Implementation with React.createRef() and handling click with node element.

class Dummy extends React.Component {
    
  constructor( props ) {
    super( props );

    this.inputFileRef = React.createRef();
    this.onFileChange = this.handleFileChange.bind( this );
    this.onBtnClick = this.handleBtnClick.bind( this );
  }

  handleFileChange( e ) {
    /*Selected files data can be collected here.*/
    console.log( e.target.files );
  }

  handleBtnClick() {
    /*Collecting node-element and performing click*/
    this.inputFileRef.current.click();
  }

  render() {
    return (
      <form className="some-container">
        <input
          type="file"
          ref={this.inputFileRef}
          onChange={this.onFileChange}
        />
        <button onClick={this.onBtnClick}>Select file</button>
      </form>
    )
  }
}
Yogi
  • 1,527
  • 15
  • 21
  • 1
    This is the right approach and rightfully should have been an accepted answer. – VinKrish Jun 18 '20 at 18:10
  • so how to save the uploaded file in state variable? I added a onChange in input but it is not working ?? – Himanshu Tariyal Oct 08 '20 at 09:06
  • 2
    handle it using onChange property in input tag. ``` e.target.files[0]} />``` [More info](https://stackoverflow.com/a/15792918/4067157) – Yogi Oct 08 '20 at 11:08
  • How do you ensure that the ref has been populated though? It doesn't trigger a render, so the first render the ref is always null in a funcctional component. Is it considered a best practice to just re-render every first time just to grab ref instances? – Joel Hager Feb 12 '21 at 10:48
  • this.onBtnClick = this.handleBtnClick.bind( this ); ====> should be ====> this.onBtnClick = this.onBtnClick.bind( this ); – Dilakshan Sooriyanathan Apr 21 '21 at 15:15
36

You don't need jQuery for this. You don't even need an event handler. HTML has a specific element for this, called label.

First, make sure your input element has an id attribute:

React.createElement('input',{type:'file', name:'myfile', id:'myfile'})

Then, instead of:

React.createElement('a',{onClick: this.doClick},'Select File')

Try:

React.createElement('label',{htmlFor: 'myfile'},'Select File')

(Instead of adding htmlFor and id attributes, another solution is to make the input element a child of the label.)

Now clicking the label should trigger the same behaviour as clicking the input itself.

Bart S
  • 1,698
  • 1
  • 16
  • 21
35

You could trigger the input type file with ref, f.e:

on your class component:

<input 
    ref={fileInput => this.fileInput = fileInput} 
    type="file"
 />
<button onClick={this.triggerInputFile}> Select File </button>

and make a function on that class component too:

triggerInputFile = () => this.fileInput.click()
Fidel Ramadhan
  • 411
  • 5
  • 5
7

Using Hooks with useref:

import React, {useRef} from 'react';
const FancyInput = () => {
    const fileInput = useRef(null)

    const handleClick = () => {
        fileInput.current.click()
    }

    const handleFileChange = event => {
        console.log("Make something")
    }

    return(
        <div className="patientactions-container">
            <input
                type="file"
                onChange={(e) => handleFileChange(e)}
                ref={fileInput} 
            />
            <div onClick={() => handleClick()}></div>
        </div>
    )
}
export default FancyInput;
Farid Murzone
  • 136
  • 1
  • 7
  • 1
    I tried this but I keep getting the error `Object is possibly 'null'.` when attempting to run `fileInput.current.click()`. I tried using an if statement to only run it if `fileInput.current !== null` but I am still getting the error. Any ideas? – manesioz Jun 18 '21 at 20:58
3

Building on the answer from @YÒGÎ , here is an implementation using TypeScript:

class Dummy extends React.Component {
    fileInputRef: React.RefObject<HTMLInputElement> = React.createRef();

    forwardClickToInputElement = () => {
        this.fileInputRef.current!.click();
    };

    handleUploadDemand = (ie: ChangeEvent<HTMLInputElement>) => {
        const fileList: FileList = ie.target.files;

        // do something with the FileList, for example:
        const fileReader = new FileReader();
        fileReader.onload = () => {
            const str = String(fileReader.result);
            try {
                const parsedContent = YOUR_OWN_PARSING(str);
            } catch (error) {
                // YOUR OWN ERROR HANDLING
            }
        };
        fileReader.readAsBinaryString(fileList[0])
    }

    render() {
        return (
            <div className="some-container">
                <button onClick={this.forwardClickToInputElement}>Select File</button>
                <input ref={this.fileInputRef} type="file" onChange={this.handleSelectFile} hidden={true}/>
            </div>
        )
    }
}

References:

  1. Solution for how to use refs in React with Typescript https://stackoverflow.com/a/50505931/2848676
  2. Use ! operator for ref type narrowing https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315
Michael Osofsky
  • 11,429
  • 16
  • 68
  • 113
0
const CustomInput = () => {
  const handleClick = () => {
    document.getElementById("file_upload").click();
  };

  const handleFileChange = (event) => {
    console.log("Make something");
  };

  return (
    <div className="patientactions-container">
      <input type="file" id="file_upload" onChange={(e) => handleFileChange(e)} />
      <div onClick={() => handleClick()}></div>
    </div>
  );
};
export default CustomInput;
0

For those who want to implement Farid's answer in Typescript, can do the following:

import React, {useRef} from 'react';
const FancyInput = () => {
  const fileInput = useRef<HTMLInputElement>(null)

  const handleClick = () => {
    if(fileInput.current) {
      fileInput.current.click()
    }
  }

  const handleFileChange = event => {
      console.log("Make something")
  }

  return(
      <div className="patientactions-container">
          <input
              type="file"
              onChange={(e) => handleFileChange(e)}
              ref={fileInput} 
          />
          <div onClick={() => handleClick()}></div>
      </div>
  )
}
export default FancyInput;
Paolo
  • 3
  • 2
0

Anyone Looking for a Simple approach try this.

<div>
  <input type="file" name="library-images" id="library-images" hidden />
     <Button type='button' onClick={()=>{
                document.getElementById("library-images")?.click()
     }}>Upload</Button>
</div>
Hypermona
  • 31
  • 3
-24

EDIT: This is a question I answered a long time ago not knowing very much react at this time. The fun thing is that it has been considered valid ^^.

So for anyone reading this answer; this answer is wrong and is a very good example of something you shouldn't do in react.

Please find below a nice anti-pattern, again, don't do it.

=================================================

You can achieve this using jQuery:

this.doClick: function() {
    $('input[type=file]').trigger('click');
}

React does not provide specific functions to trigger events, you can use jQuery or simply native Javascript: see Creating and triggering events on MDN

François Richard
  • 6,817
  • 10
  • 43
  • 78
  • React.createElement('input',{type:'file', name:'myfile'}) then the button React.creatElement('a',{onClick: this.doClick},'Select File') So how to define and trigger the INPUT FILE click event when we click the A HREF? – Yarin Nim Sep 07 '15 at 08:13
  • 7
    Yes, currently I always do like that, but what I need is to find is there any way to trigger by REACT, not by JQUERY – Yarin Nim Sep 07 '15 at 08:27
  • 6
    The reason one would use React is to get away from jQuery. This answer is poor. – insidesin Jan 16 '19 at 01:02
  • 1
    jQuery completely gives control of the DOM and react does the opposite. It's highly recommended that they should never be used together. – Aayushi Kambariya Aug 07 '20 at 04:05