Javascript 回调未引用正确的状态值

Javascript callback doesn't reference the correct state value

此应用程序显示 3 个任务列表 - 待办事项列表、进行中列表和已完成列表。 我想每秒增加进行中任务的时间,但以下代码片段不起作用。

./src/contexts/TaskProvider.tsx

/////////////////////////////////////////////////////////////////////////

import React, { createContext, useState, useContext } from 'react';

interface ITask {
  id: number;
  name: string;
  status: 'todo' | 'in-progress' | 'done';
  hourly?: number;
  time?: number;
  price?: number;
  timer?: number;
}

interface ITaskContext {
  tasks?: ITask[];
  createTask?: (name: string, hourly: number) => void;
  startTask?: (id: number) => void;
  resolveTask?: (id: number) => void;
}

const TaskContext = createContext<ITaskContext>({});

const TaskProvider = props => {
  const [tasks, setTasks] = useState<ITask[]>([]);
  const [maxId, setMaxId] = useState<number>(1);

  const createTask: (name: string, hourly: number) => void = (name, hourly) => {
    setTasks([...tasks, { id: maxId, name, status: 'todo', hourly }]);
    setMaxId(maxId + 1);
  };

  const startTask: (id: number) => void = id => {
    // const timer = 0;
    // <!!! bug here !!!>
    const timer = setInterval(() => {
      // here, tasks is different from the array at interval.
      console.log(tasks);

      const newTasks = tasks.map(item => {
        return item.id === id && item.status === 'in-progress'
          ? {
              id,
              name: item.name,
              status: 'in-progress',
              time: item.time !== undefined ? item.time + 1 : 0,
              timer: item.timer,
            }
          : item;
      });
      setTasks(newTasks as ITask[]);
    }, 1000);
    // <!!! bug code ends !!!>

    const newTasks = tasks.map(item =>
      item.id === id
        ? {
            id,
            name: item.name,
            status: 'in-progress',
            hourly: item.hourly,
            time: 0,
            timer,
          }
        : item,
    );
    setTasks(newTasks as ITask[]);
  };

  const resolveTask: (id: number) => void = id => {
    const newTasks = tasks.map(item =>
      item.id === id
        ? {
            id,
            name: item.name,
            status: 'done',
            price:
              (item.hourly ? item.hourly : 0) * (item.time ? item.time : 0),
          }
        : item,
    );
    setTasks(newTasks as ITask[]);
  };

  return (
    <TaskContext.Provider
      value={{ tasks, createTask, startTask, resolveTask }}
      {...props}
    />
  );
};

const useTasks: () => ITaskContext = () => {
  if (TaskContext !== undefined) {
    return useContext<ITaskContext>(TaskContext);
  }

  throw new Error('TaskContext must be used within a TaskProvider');
};

export { TaskProvider, useTasks };
/////////////////////////////////////////////////////////////////////////

正如您在评论中看到的,每个时间间隔的任务 ID 与数组不同。 没有这个错误片段,它可以在不增加时间的情况下正常工作。

您可以从 https://github.com/Quanshihe/react-todo-list.git

获取此项目

解决方案是使用 useRef() 挂钩,如下所示。

  ...
  const stateRef = useRef<ITask[]>();
  stateRef.current = tasks;

  const startTask: (id: number) => void = id => {
    const timer = setInterval(() => {
      const newTasks = stateRef.current?.map(item => {
        return item.id === id && item.status === 'in-progress'
          ? {
              id,
              name: item.name,
              status: 'in-progress',
              time: item.time !== undefined ? item.time + 1 : 0,
              timer: item.timer,
            }
          : item;
      });
      setTasks(newTasks as ITask[]);
    }, 1000);

    const newTasks = tasks.map(item =>
      item.id === id
        ? {
            id,
            name: item.name,
            status: 'in-progress',
            hourly: item.hourly,
            time: 0,
            timer,
          }
        : item,
    );
    setTasks(newTasks as ITask[]);
  };
  ...

结果,它起作用了!!!