You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件在MongoDB数据变更时自动重渲染的实现方案咨询

解决TodoList复选框点击后页面不同步的问题及优化方案

一、实现自动同步的核心方案:利用SWR的mutate触发数据刷新

SWR自带的mutate方法可以主动触发数据重新拉取,让组件自动同步数据库变更。具体修改如下:

  1. 在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>
      )
    }
    
  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 15:15:56