【问题标题】:How to reset ReactJS file input如何重置 ReactJS 文件输入
【发布时间】:2017-06-30 17:56:12
【问题描述】:

我有文件上传输入:

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

我以这种方式处理上传:

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);
}

如果我两次上传相同的文件,则不会触发上传事件。我该如何解决?对于简单的 js 代码,执行以下操作就足够了: this.value = null;在更改处理程序中。我怎样才能用 ReactJS 做到这一点?

【问题讨论】:

  • onChange={this.getFile.bind(this)} 或 getFile = (e) => {

标签: html reactjs file-upload


【解决方案1】:
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>
    </>
  );
}

【讨论】:

    【解决方案2】:

    在我的情况下,我有一个功能组件,在选择一个文件后,它假设将文件名设置为状态,因此使用上述任何解决方案都失败了,除了我这样修复的 ref 。

    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>
        )
    }
    

    【讨论】:

      【解决方案3】:

      我认为您可以像这样清除输入值:

      e.target.value = null;
      

      文件输入无法控制,没有 React 特定的方法可以做到这一点。


      编辑对于旧浏览器(one of the following techniques。

      参见http://jsbin.com/zurudemuma/1/edit?js,output(在 IE10 和 9 上测试)

      【讨论】:

      • 太棒了... :) 它拯救了我的一天
      • 注意:在 Chrome 79 中设置 undefined 会触发此错误: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.但是null 不会触发错误。
      • 注意浏览器不兼容问题:stackoverflow.com/questions/1703228/…
      • 谢谢@T.J.Crowder,我更新了我的答案。即使大多数网站停止支持这些浏览器,它们仍然存在 :)
      • 对于打字稿来说 // @ts-ignore 是必需的
      【解决方案4】:

      我们可以使用key = {this.state.fileInputKey} 重置文件输入,并在构造函数状态下将fileInputKey 初始化为Date.now()。 在文件上传成功时,我们需要再次分配 fileInputKey: Date.now(),所以它的值将与之前不同,并在下一个render()创建新的文件输入组件@

      我们也可以通过点击按钮来手动清除/重置文件输入

      下面是工作代码:

      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;
      

      【讨论】:

      • 清理文件按钮非常有用。谢谢
      【解决方案5】:

      以下使用 React Hooks 对我有用。这是使用所谓的“受控输入”来完成的。这意味着,输入由状态控制,或者它们的真实来源是状态。

      TL;DR 重置文件输入是一个使用useState()useRef() 挂钩的两步过程。

      注意:我还包括了如何重置文本输入以防其他人好奇。

      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>
          )
      };
      

      支持文档:

      1. useState Hook
        • 返回一个有状态的值,以及一个更新它的函数。
      2. useRef Hook
        • 如果您将 ref 对象传递给 React,React 将在相应的 DOM 节点更改时将其当前属性设置为该节点。
      3. Using files from web apps
        • 如果用户只选择一个文件,则只需要考虑列表中的第一个文件。

      【讨论】:

      • 这应该是公认的答案,因为这是重置输入的更多 React 方式。我在另一个组件中有输入,我可以很容易地使用 useRef 重置它。我不需要选择输入或收听任何事件。
      【解决方案6】:

      每次点击onClick,您都可以重置输入,这样即使使用相同的文件onChange也会被触发。

      <input onChange={this.onChange} onClick={e => (e.target.value = null)} type="file" />
      

      【讨论】:

      • 非常好的解决方案
      【解决方案7】:

      我通过在我的文件输入中更新key 来做到这一点。 这将强制重新渲染,并且之前选择的文件将消失。

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

      更改状态inputKey 将重新渲染组件。 更改inputKey 的一种方法是在单击应该清除字段的按钮时始终将其设置为Date.now()

      【讨论】:

      • 这很好用。还可以轻松地向用户提供一些自定义反馈。例如:我有一个状态变量作为键(使用 Date.now() ,并使用另一个状态变量 - 在此旁边设置 - 向用户提供消息,例如“在(时间)成功上传”。
      • 当您不想在单击按钮时清除该字段,但您想在上传任何其他文件之前确保该字段为空时,这很有效。
      【解决方案8】:

      如果您知道根本不会使用内置文件输入值,也可以将其包含在输入元素中。

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

      这样,值总是在渲染时重置为空字符串,您不必笨拙地将其包含在 onChange 函数中。

      【讨论】:

        【解决方案9】:

        这是我使用 redux 表单的解决方案

        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;
        

        【讨论】:

          【解决方案10】:

          我知道文件输入总是不受控制的,但是下面的代码在我自己的项目中仍然有效,我可以毫无问题地重置输入。

          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} />
              );
          }
          

          【讨论】:

            【解决方案11】:

            这对我有用 - ref={ref => this.fileInput = ref}

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

            在我的情况下,一旦文件上传到服务器,我使用下面的语句清除它

             this.fileInput.value = "";
            

            【讨论】:

            • 我喜欢这种方法,但是我想从 getDerivedStateFromProps 制作这个,不幸的是它不起作用,因为我们无法访问那里的 this
            【解决方案12】:

            对我有用的是为文件输入设置key 属性,然后当我需要重置它时,我会更新关键属性值:

            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>
              )
            }
            

            这会迫使 React 从头开始​​重新渲染输入。

            【讨论】:

            • 为了解释它是如何工作的,当你想要清除输入时,你需要更新 this.state.theInputKey。在引擎盖下,更改键会导致响应重新渲染输入从而清除它。
            • 我喜欢这个主意。然后我可以通过其他功能控制输入字段,这正是我现在需要的。它工作正常。谢谢。
            • 我喜欢这种方法,但是我想从getDerivedStateFromProps 制作这个,幸运的是它也可以在那里工作,因为我们仍然可以访问state。 :)
            • 这对我尝试重新渲染 react-csv-reader (不是输入标签)来解决同样的问题很有用。谢谢。
            • 工作就像一个魅力。我用key={Date.now()}
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-02-19
            • 2020-04-26
            • 2018-08-11
            • 2021-02-26
            • 2015-11-02
            • 1970-01-01
            相关资源
            最近更新 更多