如何重置 ReactJS 文件输入

Posted

技术标签:

【中文标题】如何重置 ReactJS 文件输入【英文标题】:How to reset ReactJS file input 【发布时间】: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) => 【参考方案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 不会触发错误。 注意浏览器不兼容问题:***.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>
    )
;

支持文档:

    useState Hook 返回一个有状态的值,以及一个更新它的函数。 useRef Hook 如果您将 ref 对象传递给 React,React 将在相应的 DOM 节点更改时将其当前属性设置为该节点。 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)  />
              <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()

以上是关于如何重置 ReactJS 文件输入的主要内容,如果未能解决你的问题,请参考以下文章

如何在reactjs中重置表单和登录错误消息

ReactJs 保留多个react 值,刷新后不重置状态

如何在 ReactJS 中更改单选按钮时重置选定的下拉列表

如何使用 ReactJS 将我的输入值保存到文本文件中?

如何在reactJS中显示从输入类型=文件中选择的图像

ReactJS:函数内部无法识别状态变化?