概要
input type="file"にcssを当てたいが、仕様上できない
調べたところReactの場合は、useRefを用いてinputはhiddenで非表示状態とし、
CSSを当てたボタンなどから参照するのが良さそうです。
その他、いろいろ引っかかった箇所があったので、まとめました。
環境
React 16.8.0以上
Typescript 4.1.2
基本
refオブジェクトを作成します。
// この場合、inputRefはReact.RefObject<HTMLInputElement>型
const inputRef = useRef<HTMLInputElement>(null);
Typescriptで実装する場合、useRefの返り値の型がRefObject と MutableRefObjectのどちからになります。
意図しない挙動となる場合もあるので、注意が必要です。
この記事に書いてあるように、初期値null、参照するinputの型で定義します。
import { useRef } from "react";
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
const onClickButton = () => {
inputRef.current?.click();
};
return (
<div>
<input type="file" ref={inputRef} hidden />
<br />
<button onClick={onClickButton}>ボタン</button> //ここのbuttonをデザインしてもらえれば・・・
</div>
);
}
・ 先ほど定義したinputRefをinputに結びつけます。
・ ButtonをClickしたときの関数、onClickButtonを定義します。
最低限これだけやれば、ボタンを押下時にファイルを開くダイアログが表示されているはずです。
あとは、buttonにコンポーネントライブラリを使ったり、自力でcssを書いたりしてもらえればいいと思います。
その他
選択したファイル名を表示するコンポーネントを作ってみました。(ファイルは複数選択可能)
・chakra-uiを使用しています。
・event.target.filesで読み込んだファイルは、FileList型のオブジェクトになっているので、mapは使えません。
=> Array.fromでFile型の配列にするか、for文で頑張るしかないようです。
import React, { useRef, useState } from "react";
import { IconButton } from "@chakra-ui/react";
import { AttachmentIcon } from "@chakra-ui/icons";
const InputFileButton = () => {
const inputRef = useRef<HTMLInputElement>(null);
const [images, setImages] = useState<File[]>([]);
const onButtonClick = () => {
inputRef.current?.click();
};
const onChangeImage = (event: React.ChangeEvent<HTMLInputElement>) => {
const inputImage = event.target.files; // inputImageは、この時点でFileList | null
if (inputImage != null) {
const inputArray = Array.from(inputImage);
setImages(inputArray);
}
};
return (
<>
<input
type="file"
ref={inputRef}
accept="image/*"
hidden
onChange={onChangeImage}
multiple
/>
<IconButton
aria-label="Input-image"
icon={<AttachmentIcon />}
onClick={onButtonClick}
/>
{images?.map((image) => (
<p>{image.name}</p>
))}
</>
);
};
export default InputFileButton;
初めて記事を書いてみました。
間違っている箇所、改善する点あれば、コメントよろしくお願いします。