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

React待办应用:如何阻止进度计算函数在达到100%后继续调用?

解决React待办应用中进度函数重复调用的问题

方案1:渲染层直接判断过期状态,避免无效计算

在渲染每个任务卡片时,先判断当前时间是否已超过截止日期,若过期则直接用100作为进度值,否则再调用进度计算函数。这样过期任务就不会再执行多余的计算逻辑。

修改后的代码如下:

// 先优化进度计算函数,增加提前终止逻辑
const getProgress = (dueDate, timeCreated) => {
  const now = new Date();
  const due = new Date(dueDate);
  const created = new Date(timeCreated);
  
  // 已过期直接返回100,跳过后续计算
  if (now >= due) return 100;
  
  const totalTimeDifference = due - created;
  const elapsedTime = now - created;
  const progress = Math.round((elapsedTime / totalTimeDifference) * 100);
  // 防止进度值超出100
  return Math.min(progress, 100);
}

return (
  <div className="list">
    <ul>
      {elementsList.map((element, index) => {
        // 渲染时直接判断任务是否过期
        const isExpired = new Date() >= new Date(element.dueDate);
        const progressValue = isExpired ? 100 : getProgress(element.dueDate, element.timestamp);
        
        return (
          <Card className="cardElement" variant="filled" key={index}>
            <CardBody>
              <Stack divider={<StackDivider />}>
                <Box className="listElementBody">
                  <Text>{element.text}</Text>
                  <ButtonGroup>
                    <Button colorScheme="red" onClick={() => handleDeleteClick(index)}>Delete</Button>
                    <Button colorScheme="blue" onClick={() => handleEditClick(index)}>Edit</Button>
                  </ButtonGroup>
                </Box>
                <Box>
                  <Heading fontSize='1xl'>Due in:</Heading>
                  <Text>{getTimeDifference(element.dueDate)}</Text>
                  <Progress colorScheme='green' size='lg' hasStripe value={progressValue} />
                </Box>
              </Stack>
            </CardBody>
          </Card>
        );
      })}
    </ul>
  </div>
)

方案2:持久化过期状态,彻底避免重复检查

如果希望连过期判断都只执行一次(而非每次渲染都重复判断),可以用状态存储已过期的任务ID,定时检查并更新状态,之后不再对过期任务做任何计算。

实现步骤:

  1. 给每个待办任务添加唯一ID(建议创建任务时生成,比如用Date.now() + Math.random()或uuid库,避免用index作为唯一标识);
  2. 用useState维护已过期任务的集合;
  3. 用useEffect定时检查任务是否过期,更新过期集合。

代码示例:

import { useState, useEffect } from 'react';

// 假设创建任务时已为每个element添加唯一id
const TodoList = () => {
  const [elementsList, setElementsList] = useState([]);
  const [expiredTasks, setExpiredTasks] = useState(new Set());

  // 定时检查任务过期状态
  useEffect(() => {
    const checkExpired = () => {
      const now = new Date();
      const updatedExpired = new Set(expiredTasks);
      
      elementsList.forEach(task => {
        if (!updatedExpired.has(task.id) && now >= new Date(task.dueDate)) {
          updatedExpired.add(task.id);
        }
      });
      
      // 仅当集合变化时更新状态,避免不必要的组件渲染
      if (updatedExpired.size !== expiredTasks.size) {
        setExpiredTasks(updatedExpired);
      }
    };

    // 初始检查一次
    checkExpired();
    // 每分钟检查一次,可根据需求调整频率
    const intervalId = setInterval(checkExpired, 60000);

    // 组件卸载时清除定时器
    return () => clearInterval(intervalId);
  }, [elementsList, expiredTasks]);

  const getProgress = (dueDate, timeCreated) => {
    const now = new Date();
    const due = new Date(dueDate);
    const created = new Date(timeCreated);
    
    const totalTimeDifference = due - created;
    const elapsedTime = now - created;
    const progress = Math.round((elapsedTime / totalTimeDifference) * 100);
    return Math.min(progress, 100);
  }

  return (
    <div className="list">
      <ul>
        {elementsList.map((element) => {
          const isExpired = expiredTasks.has(element.id);
          // 过期任务直接用100,不调用进度计算函数
          const progressValue = isExpired ? 100 : getProgress(element.dueDate, element.timestamp);
          
          return (
            <Card className="cardElement" variant="filled" key={element.id}>
              <CardBody>
                <Stack divider={<StackDivider />}>
                  <Box className="listElementBody">
                    <Text>{element.text}</Text>
                    <ButtonGroup>
                      <Button colorScheme="red" onClick={() => handleDeleteClick(element.id)}>Delete</Button>
                      <Button colorScheme="blue" onClick={() => handleEditClick(element.id)}>Edit</Button>
                    </ButtonGroup>
                  </Box>
                  <Box>
                    <Heading fontSize='1xl'>Due in:</Heading>
                    <Text>{getTimeDifference(element.dueDate)}</Text>
                    <Progress colorScheme='green' size='lg' hasStripe value={progressValue} />
                  </Box>
                </Stack>
              </CardBody>
            </Card>
          );
        })}
      </ul>
    </div>
  );
};

为什么之前的条件渲染没效果?

大概率是你没有持久化过期状态,每次组件渲染时都会重新执行判断逻辑,甚至依然会调用getProgress函数。比如如果只是在getProgress内部返回100,函数本身还是会被调用;或者条件渲染的逻辑写在了函数内部,而非渲染层的分支判断中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 14:05:38