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 与数组不同。
没有这个错误片段,它可以在不增加时间的情况下正常工作。
获取此项目
解决方案是使用 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[]);
};
...
结果,它起作用了!!!
此应用程序显示 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 与数组不同。 没有这个错误片段,它可以在不增加时间的情况下正常工作。
获取此项目解决方案是使用 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[]);
};
...
结果,它起作用了!!!