📕
File コンストラクタの存在を知り Storybook が喜んだ
JavaScript の File
は、思うがまま自由に new
してインスタンス化できるらしい。
const file = new File(["foo"], "bar.txt", {
type: "text/plain",
});
出典:https://developer.mozilla.org/ja/docs/Web/API/File/File
いまさら何言ってるの? と呆れられる方もいらっしゃるかもしれませんが、私はつい最近になって初めて知りました。
File 関連でよくあるパターン
これまでの経験では、File を扱うのは以下の2パターンでした。
-
<input type="file" />
でボタンのchange
イベントでファイルを受け取るとき - Drag & Drop API の
drop
イベントでファイルを受け取るとき
アプリケーション開発で出くわしたパターン
React 開発で、file オブジェクトを受け取る React コンポーネントを作りました。
type Props = {
file: File;
};
const FileViewer = ({ file }: Props) => {
// ...
};
これを Storybook を使ってコンポーネント開発していたときに、File コンストラクタの存在を知らなかったばかりに、Git コミット履歴が2〜3回くらい紆余曲折してしまいました。
Fetch でも使える
Jest で使っていたテストデータを、Storybook でも使いまわして表示テストしてみます。
静的ファイルのディレクトリパスを渡すコマンドオプション -s
は、カンマ区切りで複数指定可能でした。
yarn start-storybook -p 9009 -s public,src/libs/__tests__/testdata
# npm run start-storybook -p 9009 -s public,src/libs/__tests__/testdata
Storybook の内容は以下のような感じ。
import * as React from "react";
async function generateFile(filename: string): Promise<File> {
const res = await fetch(filename);
if (!res.ok) {
throw new Error("invalid status code:" + res.status);
}
const blob = await res.blob(); // プレーンテキストなら res.text() でもOK
return new File([blob], "羅生門.txt", { type: "text/plain" });
}
export const Demo = () => {
const [file, setFile] = React.useState<File | null>(null);
React.useEffect(() => {
let unmounted = false;
async function initializeFile() {
const generatedFile = await generateFile("/rashomon.txt");
if (!unmounted) {
setFile(generatedFile);
}
}
initializeFile();
return () => {
unmounted = true;
};
}, []);
if (!file) {
return null;
}
return <FileViewer file={file} />;
};
問題なく表示できました。
感想
慣れ親しんだ JavaScript の標準 API の中に、とりわけ新しいわけでもないのに見落としていた点があったのには驚きでした。
私と同じように、2〜3コミット紆余曲折してしまう方がいる可能性も米粒くらいあるかと思い、記事化してみました。
Discussion