React组件重新渲染后如何保留文件输入控件的已选文件名显示?
问题根因
- 条件渲染逻辑会卸载文件输入的DOM节点,切换回表单渲染时生成的是全新的控件实例,原生自带的选中状态自然丢失
- 你对
useRef的使用存在错误:原本用于绑定DOM元素的ref被你手动赋值为File对象,导致无法正确获取输入控件的DOM节点 - 出于浏览器安全限制,
<input type="file">的value属性为只读,普通的状态回填逻辑对该类控件不生效
解决方案
下面提供3种可行方案,可根据业务场景选择:
方案1:使用CSS显隐替代条件渲染(最推荐,改动最小)
不卸载文件输入控件,仅通过display属性控制显隐,控件实例一直存在,选中状态不会丢失:
import { useState, useRef } from 'react' import { Form } from 'react-bootstrap' const MyComponent = () => { const [file, setFile] = useState(undefined) const fileRef = useRef(null) const onChange = event => { setFile(event.target.files[0]) } return (<div> {/* 用display控制显隐,不卸载控件 */} <div style={{display: something ? 'none' : 'block'}}> <Form.Group controlId="myFileInput" className="mb-3"> <Form.Label>Select file</Form.Label> <Form.Control type="file" onChange={onChange} ref={fileRef}/> </Form.Group> </div> </div>) }
方案2:自定义文件名展示(兼容性最好)
如果你必须保留条件渲染的逻辑,可以不依赖原生控件自带的文件名显示,额外添加自定义文本展示已选文件:
import { useState } from 'react' import { Form } from 'react-bootstrap' const MyComponent = () => { const [file, setFile] = useState(undefined) const onChange = event => { setFile(event.target.files[0]) } if(something) { return (<div></div>) } else { return (<div> <Form.Group controlId="myFileInput" className="mb-3"> <Form.Label>Select file</Form.Label> <Form.Control type="file" onChange={onChange} /> {/* 自定义展示已选文件 */} {file && <div className="mt-1 text-muted">已选文件:{file.name}</div>} </Form.Group> </div>) } }
方案3:手动回填原生控件状态
如果需要保留原生控件的文件名显示效果,可以通过DataTransferAPI手动将存储的File对象回填到新渲染的控件中:
import { useState, useRef, useEffect } from 'react' import { Form } from 'react-bootstrap' const MyComponent = () => { const [file, setFile] = useState(undefined) const fileRef = useRef(null) const onChange = event => { setFile(event.target.files[0]) } // 控件渲染完成后自动回填已存的文件 useEffect(() => { if (fileRef.current && file) { const dataTransfer = new DataTransfer() dataTransfer.items.add(file) fileRef.current.files = dataTransfer.files } }, [file]) if(something) { return (<div></div>) } else { return (<div> <Form.Group controlId="myFileInput" className="mb-3"> <Form.Label>Select file</Form.Label> <Form.Control type="file" onChange={onChange} ref={fileRef}/> </Form.Group> </div>) } }
注:DataTransfer API兼容所有现代浏览器,无需担心兼容性问题
内容的提问来源于stack exchange,提问作者Simen Russnes
相关产品推荐
相关产品推荐

