React组件在MongoDB数据变更时自动重渲染的实现方案咨询
解决TodoList复选框点击后页面不同步的问题及优化方案
一、实现自动同步的核心方案:利用SWR的mutate触发数据刷新
SWR自带的mutate方法可以主动触发数据重新拉取,让组件自动同步数据库变更。具体修改如下:
在TodoList组件中传递
mutate函数
从useSWR的返回值中解构出mutate,并将其作为props传递给每个Todo组件:// TodoList.tsx import React from 'react'; import useSWR from 'swr'; import axios from 'axios'; import Todo from './Todo'; const fetcher = (url: string) => axios.get(url).then(res => res.data) export default function TodoList() { // 解构获取mutate方法 const { data, error, mutate } = useSWR('/api/v1/todos-all', fetcher) if (error) return <div>failed to load</div>; if (!data) return <div>loading...</div>; return ( <div> <div> <h1>To Do</h1> { data.todos.map((todo: any) => !todo.completed ? ( <Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed} mutate={mutate} // 传递mutate函数 /> ) : null ) } </div> <div> <h1>Completed</h1> { data.todos.map((todo: any) => todo.completed ? ( <Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed} mutate={mutate} // 传递mutate函数 /> ) : null ) } </div> </div> ) }修改Todo组件,移除本地状态并触发刷新
去掉Todo组件内的checked本地状态(避免与SWR数据不一致),直接使用父组件传递的completed值;在接口请求成功后调用mutate,触发SWR重新拉取数据:// Todo.tsx import React from 'react'; import styled from 'styled-components'; import axios from 'axios'; interface ITodo { title: string; id: string; completed: boolean; mutate: () => Promise<any>; // 新增mutate类型定义 } const TodoStyle = styled.div` background-color: #fff; padding: 20px; margin: 10px; border: 1px solid #eeeeee; border-radius: 10px; `; export default function Todo({ title, id, completed, mutate }: ITodo) { const handleClick = async () => { try { // 传递布尔值而非字符串,确保后端正确解析状态 await axios.put(`/api/v1/todo/${id}`, { completed: !completed }); // 请求成功后触发数据刷新 await mutate(); } catch (err) { console.error('更新任务状态失败:', err); } } return ( <TodoStyle> {title} <input type="checkbox" checked={completed} onChange={handleClick}/> </TodoStyle> ) }
二、更优的实现方案建议
1. 优化数据遍历性能
当前TodoList两次遍历todos数组,可提前过滤出待办和已完成列表,减少重复遍历:
// TodoList.tsx中,在return前添加: const pendingTodos = data.todos.filter(todo => !todo.completed); const completedTodos = data.todos.filter(todo => todo.completed); // 渲染时直接使用过滤后的数组: <div> <h1>To Do</h1> {pendingTodos.map(todo => ( <Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed} mutate={mutate} /> ))} </div> <div> <h1>Completed</h1> {completedTodos.map(todo => ( <Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed} mutate={mutate} /> ))} </div>
2. 乐观更新提升用户体验
利用SWR的乐观更新特性,在发起请求前先更新本地数据,让用户立即看到状态变化:
// Todo组件的handleClick修改为: const handleClick = async () => { // 先乐观更新本地数据,false表示不立即重新请求 mutate(prevData => { if (!prevData) return prevData; return { ...prevData, todos: prevData.todos.map(todo => todo._id === id ? { ...todo, completed: !completed } : todo ) }; }, false); try { await axios.put(`/api/v1/todo/${id}`, { completed: !completed }); // 请求成功后拉取最新数据 await mutate(); } catch (err) { console.error('更新任务状态失败:', err); // 请求失败,回滚本地数据 mutate(); } }
3. 类型安全优化
给todos添加明确的TypeScript类型,避免any类型的潜在问题:
// TodoList.tsx中定义Todo类型: interface TodoType { _id: string; title: string; completed: boolean; } // 给useSWR指定类型: const { data, error, mutate } = useSWR<{ todos: TodoType[] }>('/api/v1/todos-all', fetcher)
内容的提问来源于stack exchange,提问作者raulsm10
相关产品推荐
相关产品推荐

