Не получается прокинуть имя файла из компонента в компонент react(input type file) и установить дату завершения задачи в todo-листе
Есть два ключевых компонента: AddToDo.js и Todo.js. В первом происходит ввод через input заголовка, описания и прикрепление файла через input type="file". Во втором-непосредственное отображение этих данных в виде единой задачи todo. Тут возникает проблема: не получается отобразить имя прикрепленного в AddToDo файла в самом Todo. Пробовала через useState- выводится ошибка, так как input этого типа неконтролируемый. При помощи createRef получилось достать название в AddToDo, но прокинуть название в Todo не получается. Так же нужно установить дату истечения срока выполнения каждой задачи. Хочу доработать тудушку но не понимаю как. Заранее благодарю за помощь!!Прикрепляю ссылку на репозиторий: https://github.com/Kohlenbaron28/todo
//AddToDo.js
import React from "react";
import { db } from "../firebase";
import { collection, addDoc } from "firebase/firestore";
export default function AddTodo() {
const [title, setTitle] = React.useState("");
const [about, setAbout] = React.useState("");
const [file, setFile] = React.useState();
const handleSubmit = async (e) => {
e.preventDefault();
if (title !== "" && about !== "") {
await addDoc(collection(db, "todos"), {
title,
about,
completed: false,
});
setTitle("");
setAbout("");
}
};
const fileInput = React.createRef();
function handleChange(event) {
event.preventDefault();
console.log(`Selected file - ${fileInput.current.files[0].name}`);
setFile(fileInput.current.files[0].name);
console.log(setFile);
}
return (
<form onSubmit={handleSubmit}>
<div className="input_container">
<input
type="text"
placeholder="Enter title..."
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<input
type="text"
placeholder="Enter about..."
value={about}
onChange={(e) => setAbout(e.target.value)}
/>
<input
type="file"
ref={fileInput}
value={(e) => e.target.files}
onChange={handleChange}
/>
</div>
<div className="btn_container">
<button>Add</button>
</div>
</form>
);
}
//Todo.js
import React from "react";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
export default function Todo({ todo, toggleComplete, handleDelete, handleEdit,}) {
const [newTitle, setNewTitle] = React.useState(todo.title);
const [newAbout, setNewAbout] = React.useState(todo.about);
const [newFile, setNewFile] = React.useState(todo.setFile);
const handleChangeTitle = (e) => {
e.preventDefault();
if (todo.complete === true) {
setNewTitle(todo.title);
} else {
todo.title = "";
setNewTitle(e.target.value);
}
};
const handleChangeAbout = (e) => {
e.preventDefault();
if (todo.complete === true) {
setNewAbout(todo.about);
} else {
todo.about = "";
setNewAbout(e.target.value);
}
};
return (
<div className="todo">
<input
style={{ textDecoration: todo.completed && "line-through" }}
type="text"
value={todo.title === "" ? newTitle : todo.title}
className="list"
onChange={handleChangeTitle}
/>
<input
style={{ textDecoration: todo.completed && "line-through" }}
type="text"
value={todo.about === "" ? newAbout : todo.about}
className="list-about"
onChange={handleChangeAbout}
/>
<div>
<input type="file"/>
<button
className="button-complete"
onClick={() => toggleComplete(todo)}
>
<CheckCircleIcon id="i" />
</button>
<button
className="button-edit"
onClick={() => handleEdit(todo, newTitle, newAbout)}
>
<EditIcon id="i" />
</button>
<button className="button-delete" onClick={() => handleDelete(todo.id)}>
<DeleteIcon id="i" />
</button>
</div>
</div>
);
}
//App.js
import AddToDo from './components/AddToDo';
import './App.css';
import React from 'react';
import Title from './components/Title';
import Todo from './components/Todo';
import {
collection,
query,
onSnapshot,
doc,
updateDoc,
deleteDoc,
} from "firebase/firestore";
import { db } from "./firebase";
function App() {
const [todos, setTodos] = React.useState([]);
React.useEffect(() => {
const q = query(collection(db, "todos"));
const unsub = onSnapshot(q, (querySnapshot) => {
let todosArray = [];
querySnapshot.forEach((doc) => {
todosArray.push({ ...doc.data(), id: doc.id });
});
setTodos(todosArray);
});
return () => unsub();
}, []);
const handleEdit = async (todo, title, about) => {
await updateDoc(doc(db, "todos", todo.id), { title: title, about: about});
};
const toggleComplete = async (todo) => {
await updateDoc(doc(db, "todos", todo.id), { completed: !todo.completed });
};
const handleDelete = async (id) => {
await deleteDoc(doc(db, "todos", id));
};
return (
<div className="App">
<div>
<Title/>
</div>
<div>
<AddToDo/>
</div>
<div className='todo_container'>
{todos.map((todo) => (
<Todo
key={todo.id}
todo={todo}
toggleComplete={toggleComplete}
handleDelete={handleDelete}
handleEdit={handleEdit}
/>
))}
</div>
</div>
);
}
export default App;
Ответы (1 шт):
Если речь идет про список имен файлов, то прежде всего нужно позаботится о том чтобы они добавлялись в базу, в данном случае, я так понял это только строки имен, хранение самих файлов в firebase лучше обеспечить через store, либо их придется переводить в base64 со всеми вытекающими последствиями.
const [files, setFiles] = useState();
const fileInput = useRef(null)
const handleSubmit = async (e) => {
e.preventDefault();
if (title !== "" && about !== "") {
handleAddTodo({
title,
about,
completed: false,
files
})
setTitle("");
setAbout("");
fileInput.current.value = null; // очищаем input от файлов
}
};
handleAddTodo в данном случае находится в App где рендерится сам список
Для того чтобы можно было выбрать несколько файлов нужно использовать атрибут multiple:
<input
type="file"
multiple={true}
ref={fileInput}
onChange={handleChange}
/>
(e) => e.target.files не может быть валидным значением для value у input так как там можно хранить лишь строковые (или числовые типы, но в конкретном случае они не используются)
Помещать в стейт имена файлов можно так (preventDefault не нужен):
function handleChange({target}) {
const fileNames = Array.from(target.files).map(file => file.name)
console.log(`Selected files - ${fileNames.join(', ')}`);
setFiles(fileNames);
}
Пример вывода:
На всякий случай добавлю, что браузер сам не имеет возможности обращаться к локальным файлам если вы посмотрите на value в input то увидите что-то типа C:\fakepath\some_file.txt — и это при том что у меня linux и диск C отстутсвует по причине иной файловой системы. Поэтому если Вы впоследствии захотите взаимодействовать с содержимым файлов, вам нужно сохранять их в Облачное хранилище для Firebase и подгружать на клиент.
