如何重置 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 特定的方法可以做到这一点。
编辑对于旧浏览器(
参见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 文件输入的主要内容,如果未能解决你的问题,请参考以下文章