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

如何修复TypeScript React中“void[]无法赋值给SetStateAction<ITask[]>”错误

React TypeScript Todo应用:点击Complete按钮页面不更新的问题修复

问题描述

用TypeScript开发Todo列表应用时,点击任务的「Complete」按钮后,控制台显示数据已更改,但页面没有更新,同时出现TypeScript错误:

TS2345: Argument of type 'void[]' is not assignable to parameter of type 'SetStateAction<ITask[]>'.
  Type 'void[]' is not assignable to type 'ITask[]'.
    Type 'void' is not assignable to type 'ITask'.

错误原因

  1. map函数无返回值:completeTask中使用map时未返回每个任务对象,导致生成void[]数组,不符合ITask[]类型要求,触发TS报错。
  2. 直接修改原状态对象:直接执行task.completed = true属于篡改原状态引用,React无法检测到状态变化,因此不会触发组件重渲染。
  3. 使用数组索引作为任务ID:传递给TodoItem的id用的是map的索引值,而非任务自身的id,列表增删时会导致索引与实际任务不匹配。

修复步骤

1. 修正completeTask函数,生成新状态对象

使用map遍历任务列表时,对目标任务返回新对象(避免修改原状态),其他任务直接返回;同时用函数式更新setTodos,确保获取最新状态:

const completeTask = (id: number) => {
  setTodos(prevTodos => 
    prevTodos.map(task => 
      task.id === id ? {...task, completed: true} : task
    )
  );
}

2. 传递任务自身ID替代索引

渲染TodoItem时,使用task.id作为id属性和completeTask的参数,同时将task.id设为React列表的key:

{todos.map((task) => (
  <TodoItem 
    key={task.id}
    id={task.id} 
    todo={task.todo} 
    completed={task.completed} 
    completeItem={() => completeTask(task.id)} 
    startDate={task.startDate} 
    endDate={task.endDate ?? ""} 
  />
))}

3. 优化TodoItem视觉反馈(可选)

原代码中completed为true时仅添加透明边框,视觉无变化,可改为更直观的样式:

<div className={`bg-white rounded-[10px] flex flex-col w-[300px] p-[18px] h-[270px] justify-between m-10 shadow-[2px_4px_20px_0px_rgba(0,0,0,0.04)] ${completed ? 'bg-gray-100 line-through' : ''}`}>

完整修正代码

App.tsx

import React, { FC, useState } from 'react';
import { TodoItem } from './components/TodoItem';

interface ITask {
  id: number;
  todo: string;
  startDate: string;
  endDate?: string;
  completed: boolean;
}

const App: FC = () => {
  const [todos, setTodos] = useState<ITask[]>([
    {
      id: 0,
      todo: "Buy Bread",
      startDate: "12/12/2023",
      endDate: "12/14/2023",
      completed: false
    }, {
      id: 1,
      todo: "Buy House",
      startDate: "12/12/2023",
      endDate: "12/13/2023",
      completed: false
    }
  ]);

  const completeTask = (id: number) => {
    setTodos(prevTodos => 
      prevTodos.map(task => 
        task.id === id ? {...task, completed: true} : task
      )
    );
  }

  return (
    <>
      {todos.map((task) => (
        <TodoItem 
          key={task.id}
          id={task.id} 
          todo={task.todo} 
          completed={task.completed} 
          completeItem={() => completeTask(task.id)} 
          startDate={task.startDate} 
          endDate={task.endDate ?? ""} 
        />
      ))}
    </>
  );
}

export default App;

TodoItem.tsx

import React, { FC } from 'react';

export interface ITodoItemProps {
  id: number;
  todo: string;
  startDate: string;
  endDate?: string;
  completed: boolean;
  completeItem: (id: number) => void;
}

export const TodoItem: FC<ITodoItemProps> = ({ id, todo, startDate, endDate, completeItem, completed }) => {
  return (
    <div className={`bg-white rounded-[10px] flex flex-col w-[300px] p-[18px] h-[270px] justify-between m-10 shadow-[2px_4px_20px_0px_rgba(0,0,0,0.04)] ${completed ? 'bg-gray-100 line-through' : ''}`}>
      <div>
        <div className='flex flex-row gap-3 mb-4 align-middle'>
          <img src="images/calendar.svg" className='w-[20px]' alt="calendar" />
          <p className='pb-0 mb-0 font-mono text-[12px] align-middle text-third mt-1'>{startDate}</p>
          <img src="images/arrow.svg" alt="arrow" />
          <p className='pb-0 mb-0 font-mono text-[12px] align-middle text-third mt-1'>{endDate}</p>
        </div>
        <p className='text-[17px] leading-[25px] max-h-[150px] overflow-y-auto'>{todo}</p>
      </div>
      <div className='w-full py-[8px] px-[12px] bg-second rounded-[10px] flex flex-row justify-between'>
        <img src="images/edit.svg" alt="edit" className='hover:cursor-pointer' />
        <div className='flex flex-row'>
          <button className='text-third px-[12px]'>Delete</button>
          <button onClick={() => completeItem(id)} className="bg-white px-[12px] py-[4px] rounded-[10px]">Complete</button>
        </div>
      </div>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者Caleb Gross

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:35:25