99

I have file upload input:

<input onChange={this.getFile} id="fileUpload" type="file" className="upload"/>

And I handle upload this way:

getFile(e) {
    e.preventDefault();
    let reader = new FileReader();
    let file = e.target.files[0];
    reader.onloadend = (theFile) => {
        var data = {
            blob: theFile.target.result, name: file.name,
            visitorId:  this.props.socketio.visitorId
        };
        console.log(this.props.socketio);
        this.props.socketio.emit('file-upload', data);
    };
    reader.readAsDataURL(file);
}

If I upload same file twice, then upload event is not fired. How can I fix that? For simple js code it was enough to do the following: this.value = null; in change handler. How can I do it with ReactJS?

hlfrmn
  • 3,435
  • 1
  • 25
  • 33
Stepan Yakovenko
  • 7,404
  • 22
  • 105
  • 187

12 Answers12

143

I think you can just clear the input value like this :

e.target.value = null;

File input cannot be controlled, there is no React specific way to do that.


Edit For old browsers (<IE11), you can use one of the following techniques.

See http://jsbin.com/zurudemuma/1/edit?js,output (tested on IE10 & 9)

Freez
  • 6,371
  • 2
  • 18
  • 26
  • awesome... :) it saved my day – DirtyMind Dec 20 '17 at 20:35
  • 2
    Note: setting `undefined` in Chrome 79 triggers this error: `Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.`. **But `null` doesn't trigger the error.** – Matthias Feb 12 '20 at 20:56
  • Beware browser incompatibilities on this: https://stackoverflow.com/questions/1703228/how-can-i-clear-an-html-file-input-with-javascript – T.J. Crowder Aug 20 '20 at 08:50
  • Thanks @T.J.Crowder, I updated my answer. Even if most of websites stopped supporting those browsers they still exist :) – Freez Aug 23 '20 at 02:47
  • For some reason I thought I'd seen something there saying `= null` didn't work on IE11, but you're right, it **does** in IE11. So near-universal (now) in anything people should really need to support. – T.J. Crowder Aug 23 '20 at 07:47
  • 3
    for typescript a // @ts-ignore is required – TacB0sS May 04 '21 at 22:21
57

What worked for me was setting a key attribute to the file input, then when I needed to reset it I update the key attribute value:

functionThatResetsTheFileInput() {
  let randomString = Math.random().toString(36);

  this.setState({
    theInputKey: randomString
  });
}

render() {
  return(
    <div>
      <input type="file"
             key={this.state.theInputKey || '' } />
      <button onClick={this.functionThatResetsTheFileInput()} />
    </div>
  )
}

That forces React to render the input again from scratch.

Tonatiuh
  • 1,785
  • 1
  • 17
  • 19
  • To explain how this works, you need to update the this.state.theInputKey when you want to cleat the input. Under the hood, changing the key causes react to re-render the input thus clearing it. – Jaspal Singh Dec 06 '17 at 21:54
  • 1
    I like this idea. Then I can control the input field from other functionalities, which is what I need right now. It works fine. Thanks. – Alexander Falk Sep 05 '18 at 09:12
  • I like the approach, however I wanted to make this from `getDerivedStateFromProps`, and fortunately it works there as well, cause we still have access to the `state`. :) – pesho hristov Feb 04 '19 at 12:39
  • This worked for me trying to re render react-csv-reader (not an input tag) for the same issue faced. Thanks. – Yasith Prabuddhaka Sep 02 '19 at 13:00
  • 3
    Worked like a charm. I used `key={Date.now()}` – Drusto Oct 16 '19 at 11:22
48

This work for me - ref={ref => this.fileInput = ref}

<input id="file_input_file" type="file" onChange={(e) => this._handleFileChange(e)} ref={ref=> this.fileInput = ref} />

then in my case once the file was uploaded to the server , I clear it by using the statement below

 this.fileInput.value = "";
Jozcar
  • 916
  • 9
  • 11
  • I like the approach, however I wanted to make this from `getDerivedStateFromProps` and unfortunately it won't work, as we don't have access to `this` there. – pesho hristov Feb 04 '19 at 12:38
  • This approach works for me I did this in functional React component: `const [fileInput_ref, setFileInputRef] = useState({});` and in input itself `ref={(ref) => { setFileInputRef(ref) }}` and then clear it out. Thanks @Jozcar – Yogesh Koli Mar 10 '22 at 13:14
43

I do it by updating key inside my file input. This will force a re-render and previously selected file will go away.

<input type="file" key={this.state.inputKey} />

Changing the state inputKey will re-render the component. One way to change the inputKey will be to always set it to Date.now() on click of a button which is supposed to clear the field.

aelor
  • 10,430
  • 3
  • 30
  • 46
  • This works really well. Also makes it easy to provide some custom feedback to the user. For example: I have one state variable to act as a key (using `Date.now()` , and use another state variable - set alongside this - to provide a message to the user such as 'successfully uploaded at (time)'. – backslash May 10 '20 at 03:52
  • This works well when you do not want to clear the field when you click on a button, but you want to make sure the field is empty before uploading any other file. – Hervera Sep 15 '21 at 06:23
15

With every click onClick you can reset the input, so that even with the same file onChange will be triggered.

<input onChange={this.onChange} onClick={e => (e.target.value = null)} type="file" />
Elnoor
  • 3,023
  • 3
  • 23
  • 36
13

The following worked for me using React Hooks. This is done using what is known as a "controlled input". That means, the inputs are controlled by state, or their source of truth is state.

TL;DR Resetting the file input was a two-step process using both the useState() and useRef() hooks.

NOTE: I also included how I reset a text input in case anyone else was curious.

function CreatePost({ user }) {
    const [content, setContent] = React.useState("");
    const [image, setImage] = React.useState(null); //See Supporting Documentation #1
    const imageInputRef = React.useRef(); //See Supporting Documentation #2

    function handleSubmit(event) {
        event.preventDefault(); //Stop the pesky default reload function
        setContent(""); //Resets the value of the first input - See #1

        //////START of File Input Reset
        imageInputRef.current.value = "";//Resets the file name of the file input - See #2
        setImage(null); //Resets the value of the file input - See #1
        //////END of File Input Reset
    }

    return (
    <div>
        <form onSubmit={handleSubmit}>
            <input 
            type="text" 
            placeholder="Add Post Content" 
            onChange={event => setContent(event.target.value)}
            value={content} //Make this input's value, controlled by state
            />
            <input 
            type="file"
            onChange={event => setImage(event.target.files[0])} //See Supporting Doc #3
            ref={imageInputRef} //Apply the ref to the input, now it's controlled - See #2
            />
            <button type="submit">Submit Form</button>
        </form>
    </div>
    )
};

Supporting Documentation:

  1. useState Hook
    • Returns a stateful value, and a function to update it.
  2. useRef Hook
    • If you pass a ref object to React, React will set its current property to the corresponding DOM node whenever that node changes.
  3. Using files from web apps
    • If the user selects just one file, it is then only necessary to consider the first file of the list.
Dani Amsalem
  • 856
  • 11
  • 23
  • 2
    This should be the accepted answer as this is more React way of resetting the input. I have the input in another component and I could just reset it very easily using useRef. I didn't need to select the input or listen to any events. – Zihad Ul Islam Jul 06 '21 at 12:39
6

You can also include this in your input element if you know you are not going to be using the built-in file input value at all.

<input value={""} ... />

This way the value is always reset to the empty string on render and you don't have to include it awkwardly in an onChange function.

IRTrapGod
  • 109
  • 2
  • 6
6
import React, { useRef } from "react";

export default function App() {
  const ref = useRef();

  const reset = () => {
    ref.current.value = "";
  };

  return (
    <>
      <input type="file" ref={ref} />
      <button onClick={reset}>reset</button>
    </>
  );
}
Stepan Yakovenko
  • 7,404
  • 22
  • 105
  • 187
Pooja
  • 405
  • 3
  • 13
2

I know file input is always uncontrolled however the following code still works in my own porject, I can reset the input with no problems at all.

constructor(props) {
    super(props);
    this.state = {
        selectedFile: undefined,
        selectedFileName: undefined,
        imageSrc: undefined,
        value: ''
    };

    this.handleChange = this.handleChange.bind(this);
    this.removeImage = this.removeImage.bind(this);
}

handleChange(event) {
    if (event.target.files[0]) {
        this.setState({
            selectedFile: event.target.files[0],
            selectedFileName: event.target.files[0].name,
            imageSrc: window.URL.createObjectURL(event.target.files[0]),
            value: event.target.value,
        });
    }
}

// Call this function to reset input
removeImage() {
    this.setState({
        selectedFile: undefined,
        selectedFileName: undefined,
        imageSrc: undefined,
        value: ''
    })
}

render() {
    return (
        <input type="file" value={this.state.value} onChange={this.handleChange} />
    );
}
Jack L.
  • 21
  • 4
2

We can reset file input by using key = {this.state.fileInputKey} and initialsing fileInputKey to Date.now() in constructor state. On file upload success , we need to again assign fileInputKey: Date.now(), so it will have different value than previous and it create new file input component on next render()

We can also do this manually by clicking button to clear/reset file Input

Below is the working code :

import React from "react";
import { Button } from "reactstrap";

class FileUpload extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selectedFile: null,
      fileInputKey: Date.now(),
      message: ""
    };
    this.handleClear = this.handleClear.bind(this);
    this.onClickHandler = this.onClickHandler.bind(this);
    this.onChangeHandler = this.onChangeHandler.bind(this);
  }

  onChangeHandler = event => {
    this.setState({
      selectedFile: event.target.files
    });
  };

  onClickHandler = () => {
    if (this.state.selectedFile === null) {
      this.setState({
        message: "Please select File"
      });
      return;
    }
    //axios POST req code to send file to server
    {
      /**        
         const data = new FormData()
    data = this.state.selectedFile[0]                
      axios.post("http://localhost:8080/api/uploadFile/", data)
               .then(res => { 
             if (res.status == 200) {
           // upload success
             }                
               })
               .catch(err => { 
                  //message upload failed
               })    
    */
    }
//after upload to server processed

    this.setState({
      selectedFile: null,
      fileInputKey: Date.now(),
      message: "File Uploaded"
    });
  };

  handleClear() {
    this.setState({
      selectedFile: null,
      fileInputKey: Date.now(),
      message: ""
    });
  }

  render() {
    return (
      <div>
        <input
          type="file"
          key={this.state.fileInputKey}
          class="form-control"
          onChange={this.onChangeHandler}
        />
        <button
          type="button"
          class="btn btn-success btn-block"
          onClick={this.onClickHandler}
        >
          Upload
        </button>
        <Button
          type="button"
          value="Clear"
          data-test="clear"
          onClick={this.handleClear}
        >
          {" "}
          Clear{" "}
        </Button>

        <br />
        <label>{this.state.message}</label>
      </div>
    );
  }
}

export default FileUpload;
dehart
  • 1,326
  • 14
  • 18
Vivek Bari
  • 31
  • 3
1

Here is my solution using redux form

class FileInput extends React.Component {
  constructor() {
    super();

    this.deleteImage = this.deleteImage.bind(this);
  }

  deleteImage() {
    // Just setting input ref value to null did not work well with redux form
    // At the same time just calling on change with nothing didn't do the trick
    // just using onChange does the change in redux form but if you try selecting
    // the same image again it doesn't show in the preview cause the onChange of the
    // input is not called since for the input the value is not changing
    // but for redux form would be.

    this.fileInput.value = null;
    this.props.input.onChange();
  }

  render() {
    const { input: { onChange, value }, accept, disabled, error } = this.props;
    const { edited } = this.state;

    return (
      <div className="file-input-expanded">
        {/* ref and on change are key properties here */}
        <input
          className="hidden"
          type="file"
          onChange={e => onChange(e.target.files[0])}
          multiple={false}
          accept={accept}
          capture
          ref={(input) => { this.fileInput = input; }}
          disabled={disabled}
        />
        {!value ?
          {/* Add button */}
          <Button
            className="btn-link action"
            type="button"
            text="Add Image"
            onPress={() => this.fileInput.click()}
            disabled={disabled}
          />
          :
          <div className="file-input-container">
            <div className="flex-row">
              {/* Image preview */}
              <img src={window.URL.createObjectURL(value)} alt="outbound MMS" />
              <div className="flex-col mg-l-20">
                {/* This button does de replacing */}
                <Button
                  type="button"
                  className="btn-link mg-b-10"
                  text="Change Image"
                  onPress={() => this.fileInput.click()}
                  disabled={disabled}
                />
                {/* This button is the one that does de deleting */}
                <Button
                  type="button"
                  className="btn-link delete"
                  text="Delete Image"
                  onPress={this.deleteImage}
                  disabled={disabled}
                />
              </div>
            </div>
            {error &&
              <div className="error-message"> {error}</div>
            }
          </div>
        }
      </div>
    );
  }
}

FileInput.propTypes = {
  input: object.isRequired,
  accept: string,
  disabled: bool,
  error: string
};

FileInput.defaultProps = {
  accept: '*',
};

export default FileInput;
0

In my case I had a functional component and after selecting a file it suppose to set the file name in the state so using any solution above was failing except the ref one which i fixed like this.

const fileUpload = props => {

    const inputEl = useRef(null)
    const onUpload = useCallback(e => {
         uploadFile(fileDetails)
            .then(res => {
                 inputEl.current.value = ''  
            })
            .catch(err => {
                 inputEl.current.value = ''
            })
    })
    
    return (
        <input type='file' ref={inputEl} onChange={handleChange} />
        <Button onClick={onUpload}>Upload</Button>
    )
}
dev_khan
  • 675
  • 7
  • 16