Как передать файл между компонентами React: Zustand и Redux
При загрузке файлов через <input type="file"> разработчики часто сталкиваются с проблемой передачи объекта File другим компонентам. Сохранить event.target.files[0] в глобальном стейт-менеджере (Zustand, Redux) не получается, так как File не сериализуется - его нельзя представить в виде строки JSON. В этой статье мы объясняем, почему так происходит, и предлагаем проверенные решения.
Почему File нельзя сохранить в Redux или Zustand
Объект File наследуется от Blob и содержит бинарные данные. Стейт-менеджеры, такие как Redux и Zustand, по умолчанию сериализуют состояние в JSON. File не имеет стандартного JSON-представления - при попытке вызвать JSON.stringify(file) вы получите пустой объект {}. Это приводит к потере данных при передаче.
Решение: хранить File в useState и передавать пропсами
Самый простой и надёжный способ - использовать локальный useState в родительском компоненте и передавать файл дочерним элементам через пропсы. Это не «ужасное решение», а стандартная практика React, которая сохраняет ссылку на бинарный объект. Если структура компонентов становится слишком глубокой, подумайте о рефакторинге с использованием Context API или кастомного хука.
Пример с Context API
const FileContext = React.createContext();
function FileProvider({ children }) {
const [file, setFile] = useState(null);
return (
<FileContext.Provider value={{ file, setFile }}>
{children}
</FileContext.Provider>
);
}Такой подход позволяет избежать «пробрасывания пропсов» (prop drilling) и работает с любыми бинарными данными.
Альтернатива: преобразование File в base64
Если вам обязательно нужно хранить файл в глобальном сторе (например, для сохранения между перезагрузками страницы), конвертируйте его в строку base64 с помощью FileReader:
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
}После конвертации вы можете сохранить строку base64 в Zustand или Redux, а при необходимости восстановить файл (например, для отображения превью). Недостаток: увеличение размера данных на ~33% и возможные проблемы с производительностью для больших файлов.
Использование Zustand с File: практический подход
Zustand не требует сериализации, если вы храните файл в состоянии без промежуточного преобразования. Просто сохраните ссылку на объект File:
import { create } from 'zustand';
const useFileStore = create((set) => ({
file: null,
setFile: (file) => set({ file }),
}));Это работает, потому что Zustand не сериализует состояние автоматически - данные остаются в памяти как JavaScript-объекты. Важно: при использовании persist middleware (для localStorage) вам придётся конвертировать файл в base64, так как localStorage поддерживает только строки.
Рекомендации по архитектуре
- Для небольших проектов: используйте
useStateи пропсы - это просто и предсказуемо. - Для средних проектов: внедрите Context API или Zustand без persist.
- Для крупных приложений: рассмотрите хранение файлов в IndexedDB через библиотеки типа
idb-keyval, а в сторе держите только метаданные (id, имя, размер).
Помните: File - это ссылка на бинарные данные в памяти браузера. Не пытайтесь сериализовать его в JSON, а работайте с ним как с объектом. Выберите подход, который соответствует вашим требованиям к персистентности и производительности.