React Firebase待办应用:按created字段排序任务失败求助
问题:React + Firebase 待办应用排序与过滤失效
我开发的React+Firebase待办清单应用无法实现过滤任务后按created字段排序的功能,尝试在数据库查询中添加orderBy("created")但未生效,甚至未过滤时新增任务的顺序也不对。
尝试过的查询语句:
const q = query(collection(db, 'tasks'), where("uid", "==", user?.uid), orderBy("created"));
完整代码(不含导入):
export default function Todo() { const [name, setName] = useState(""); const [tasks, setTasks] = useState([]); const [user, loading, error] = useAuthState(auth); const [searchInput, setSearchInput] = useState(""); const [filteredTasks, setFilteredTasks] = useState([]); // 获取任务数据 useEffect(() => { const q = query(collection(db, 'tasks'), where("uid", "==", user?.uid)); onSnapshot(q, (querySnapshot) => { setTasks(querySnapshot.docs.map(doc => ({ id: doc.id, data: doc.data() }))) }) }, []) // 过滤任务 useEffect(() => { setFilteredTasks(tasks .filter(task => task.data.name.match(searchInput)) ); }, [searchInput, tasks]) // 主输入框值监听 function handleChange(e) { e.preventDefault(); setName(e.target.value); console.log(name); } // 搜索输入框值监听 function handleSearchChange(e) { e.preventDefault(); setSearchInput(e.target.value) } // 添加任务到Firestore const handleAdd = async (e) => { e.preventDefault(); if (name === "") { alert("Please enter some text"); clearInput(); return; } try { await addDoc(collection(db, 'tasks'), { name: name, completed: false, created: Timestamp.now(), uid: user?.uid, }) clearInput(); } catch (err) { alert(err) } } // 双击标记任务完成 function handleClick(e) { if (e.detail === 2) { console.log("double click"); e.currentTarget.classList.toggle('double-clicked'); } } // 更新任务名称 const handleUpdate = async (taskName, id) => { let name = prompt("Please enter a new name", taskName); if (name === null) { return; } const taskDocRef = doc(db, 'tasks', id) try { await updateDoc(taskDocRef, { name: name, }) clearInput(); } catch (err) { alert(err) } } // 删除任务 const handleDelete = async (id) => { console.log(id); const taskDocRef = doc(db, 'tasks', id) try { await deleteDoc(taskDocRef) clearInput(); } catch (err) { alert(err) } } // 清空并聚焦输入框 function clearInput() { let input = document.querySelector("input"); input.value = ''; input.focus(); setName(""); } return ( <div> <div> <div> <h1>To Do List App</h1> <p>Double click to mark an item off.</p> </div> <input id="input" type="text" value={name} onChange={handleChange} autoFocus /> <button className="add-button" type="submit" onClick={handleAdd} > <IoMdAddCircle /> </button> </div> <ol> {filteredTasks.map(task => ( <li className="task-list-items" id={task.id} key={task.id} completed={task.data.completed} onClick={handleClick} > {task.data.name} <button className="edit-button" onClick={() => handleUpdate(task.data.name, task.id)} > <BsFillPencilFill /> </button> <button className="delete-button" onClick={() => handleDelete(task.id)} > <BsFillTrashFill /> </button> </li> ))} </ol> <div> <h5>Search for an item by name</h5> <input id="search-bar" type="text" value={searchInput} onChange={handleSearchChange} /> </div> </div> ); };
数据库结构:每个任务包含name、completed、created(时间戳类型)、uid字段。
解决方案
1. 修复初始数据查询的排序逻辑
你的初始查询未添加orderBy("created"),且useEffect依赖数组为空,导致user加载完成后不会重新触发查询(初始user?.uid可能为undefined)。修改后的代码:
useEffect(() => { // 确保user存在再发起查询 if (!user) return; const q = query(collection(db, 'tasks'), where("uid", "==", user.uid), orderBy("created")); const unsubscribe = onSnapshot(q, (querySnapshot) => { setTasks(querySnapshot.docs.map(doc => ({ id: doc.id, data: doc.data() }))) }) // 组件卸载时取消订阅 return unsubscribe; }, [user])
2. 过滤后维持排序规则
当前过滤逻辑仅做了筛选,未显式维持排序(查询已排序但过滤后需确保顺序),同时优化搜索匹配逻辑:
useEffect(() => { const result = tasks .filter(task => { // 空搜索时返回所有任务,同时实现大小写不敏感匹配 if (!searchInput) return true; return task.data.name.toLowerCase().includes(searchInput.toLowerCase()); }) // 按created时间戳升序排序(最新任务在最后;需最新在前则添加.reverse()) .sort((a, b) => a.data.created.seconds - b.data.created.seconds); setFilteredTasks(result); }, [searchInput, tasks])
3. 创建Firestore复合索引
同时使用where和orderBy时,Firestore需要对应的复合索引。如果查询报错,控制台会提供直接创建索引的链接,点击后按提示完成创建即可。
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

