不変なファイル処理の必要性
JavaScriptでのBlobやFileの操作は、しばしば意図しないデータの変更を引き起こします。例えば:
// 伝統的なファイルデータ処理のリスク
const fileData = { blob: new Blob(['test']), name: 'data.txt' };
const processedData = fileData;
processedData.name = 'modified.txt';
console.log(fileData.name); // 意外にも'modified.txt'に変更される
Immutable.jsの不変データ構造を使用することで、データの不変性を保証し、操作の安全性を向上させることができます。
Immutable.jsの基本機能
Immutable.jsの主なデータ構造は以下の通りです:
- OrderedSet(順序付きセット)
- Map(キー-値ペアの集合)
- Record(固定プロパティを持つオブジェクト)
これらの構造は、データの更新時に寛大なコピーを実施することでパフォーマンスを最適化します。
Blobデータの不変性管理
Blobデータを安全に管理する方法:
import { Map } from 'immutable';
// 不変なBlobオブジェクト作成
const immutableBlob = Map({
data: new Blob(['Hello, Immutable!'], { type: 'text/plain' }),
size: 16,
type: 'text/plain'
});
// 安全なプロパティアクセス
console.log(immutableBlob.get('type')); // 'text/plain'
// 更新操作は新オブジェクトを返す
const newBlob = immutableBlob.set('name', 'immutable-file.txt');
console.log(immutableBlob.has('name')); // false(元のオブジェクトは変更されない)
Fileオブジェクトの不変性管理
Fileの操作を安全に行う方法:
import { fromJS } from 'immutable';
// Fileアップロードハンドラ
function handleFileUpload(file) {
return fromJS({
fileData: file,
metadata: {
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified
},
status: 'pending'
});
}
// 使用例
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
const immutableFile = handleFileUpload(file);
// 不変性を保持したまま処理を続行
const processedFile = immutableFile.set('status', 'processed');
});
ファイル操作のユーティリティ関数
以下のユーティリティ関数を使用してファイル操作を簡素化します:
import { Map, List } from 'immutable';
const FileUtils = {
createImmutableFile(file) {
return Map({
raw: file,
info: Map({
name: file.name,
type: file.type,
size: file.size,
lastModified: file.lastModified
}),
content: null
});
},
async readFileContent(immutableFile) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
// 嵌套されたプロパティを更新
const updatedFile = immutableFile.setIn(['content'], e.target.result);
resolve(updatedFile);
};
reader.readAsText(immutableFile.get('raw'));
});
},
mergeFiles(files) {
return List(files.map(this.createImmutableFile));
}
};
export default FileUtils;
応用例
1. ファイルアップロード管理
import { List } from 'immutable';
import FileUtils from './utils/fileUtils';
class FileUploadManager {
constructor() {
this.uploadQueue = List();
}
addFile(file) {
const immutableFile = FileUtils.createImmutableFile(file);
this.uploadQueue = this.uploadQueue.concat([immutableFile]);
}
removeFile(index) {
this.uploadQueue = this.uploadQueue.delete(index);
}
async processAllFiles() {
return Promise.all(
this.uploadQueue.map(file =>
FileUtils.readFileContent(file)
).toArray()
);
}
}
2. ファイルの検索とフィルター
function filterFilesByType(fileList, mimeType) {
return fileList.filter(file =>
file.getIn(['info', 'type']).startsWith(mimeType)
);
}
function searchFiles(fileList, query) {
const lowerQuery = query.toLowerCase();
return fileList.filter(file =>
file.getIn(['info', 'name']).toLowerCase().includes(lowerQuery)
);
}
性能最適化とベストプラクティス
- データがアプリケーションに入ってきた段階で不変オブジェクトに変換してください
- 大規模なデータ操作時はwithMutationsメソッドを使用してください
- 大容量ファイルの場合は参照のみを格納するようにしてください
- 操作履歴を維持してリダオ操作を可能にします
結論
Immutable.jsを使用することで、以下のメリットを享受できます:
- データの不変性保証
- 予測可能な状態管理
- 効率的なネストデータ操作
- 安全な並列操作
Immutable.jsはBlobやFileを直接サポートしていませんが、今回紹介した方法で安全で効率的なファイル処理を実現できます。
詳細なAPI情報は公式ドキュメントやソースコードを参照してください。