兼容性还有待测试,刚换了个电脑,环境还没配完……反正在不同的浏览器里是不太一样的,主要是在输入框的显示上,因为原生的实现在这一块不太一样;但是用还是能用的。


今天下午突然收到私信,有人问我怎么在React里用TypeScript实现上传整个文件夹。其实这个还算是一个比较常见的场景,但是我并不太熟悉React,平时都是用Vue的,一时间还是有点懵。好在他提供了一个demo,大概长这样,就不写全了,意思到了就行:

<input type='file' ref='customAttributes'/>

componentDidMount(){
  var input = ReactDOM.findDOMNode(this.refs.customAttributes)
  input.setAttribute('webkitdirectory', '')
  input.setAttribute('directory', '')
  input.setAttribute('multiple', '')
}

看起来还是不错的(而且长得还有点像Vue),但是很不幸的是,这个API已经过时了。以下来自React中文网:

如果你之前使用过 React,你可能了解过之前的 API 中的 string 类型的 ref 属性,例如 "textInput"。你可以通过 this.refs.textInput 来访问 DOM 节点。我们不建议使用它,因为 string 类型的 refs 存在 一些问题。它已过时并可能会在未来的版本被移除。

那么,怎么解决呢?其实还是比较简单的,使用createRef方法创建一个可响应的ref。然后,因为用的是ts,可能需要在类型上做一些处理。第一次写,可能不是很优雅,请读者见谅:

class FileUploader extends React.Component {
    private readonly uploader: React.RefObject<HTMLInputElement>;

    constructor(props: {}) {
        super(props);
        this.uploader = React.createRef();
    }

    componentDidMount() {
        this.uploader.current!.setAttribute('webkitdirectory', '');
        this.uploader.current!.setAttribute('directory', '');
        this.uploader.current!.setAttribute('multiple', '');
    }

    render() {
        return (
            <input type='file' ref={this.uploader}/>
        );
    }
}

mount方法里出现的!是ts的非空断言,表示前面的元素一定不为空值;因为ref绑定的DOM元素可能是空值,比如还没加载,或者已经被销毁了。我们这里在mount里调用的时候,是一定不为空值的,就可以通过这个来绕开ts的空值检查。

但是这个看起来还是有点臃肿。为了一个简单的input,需要写这么多代码,尤其是constructor,很明显跟我们的目的无关。那么,能不能省略呢?答案显然是可以的。其实我比较喜欢React的一点就是它的函数式组件,而且有了hook之后,在写的时候就更有意思了:

function FunctionalFileUploader() {
    const uploader = useCallback((node: HTMLInputElement) => {
        node.setAttribute('webkitdirectory', '');
        node.setAttribute('directory', '');
        node.setAttribute('multiple', '');
    }, []);

    return (
        <input type='file' ref={uploader}/>
    )
}

用了个useCallback的hook,明显要比之前的优雅很多,而且还可以避免空值判断(虽然这个只是单纯的类型层面的事情)。

事情到了这里就算是结束了。再多嘴一句,想起Vue 3的语法,和这个简直如出一辙。果然前端大融合是个趋势啊……

Logo

前往低代码交流专区

更多推荐