React + TypeScript實現上傳整個文件夾

兼容性還有待測試,剛換了個電腦,環境還沒配完……反正在不同的瀏覽器裏是不太一樣的,主要是在輸入框的顯示上,因爲原生的實現在這一塊不太一樣;但是用還是能用的。


今天下午突然收到私信,有人問我怎麼在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的語法,和這個簡直如出一轍。果然前端大融合是個趨勢啊……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章