与 React useState 和状态更新函数中的先前状态一起使用时如何定义 TypeScript 类型?

How to define TypeScript types when used with React useState and the previous state inside a state updating function?

我是开发新手,需要一些帮助和耐心。我正在将 React 项目转换为 TypeScript,但在管理和更新 Context 组件内的状态时遇到了一些问题。

我正在构建一个日志查看器,它显示一系列任务及其嵌套的子任务,具体取决于选择的任务。

事件在事件 class 中定义,其中填充了来自自定义 useFetch 挂钩的数据。这些作为对象存储在事件数组中。选择任务后,它会存储在当前 'selectedTask' 状态,并且还必须被推入 'Hierarchy' 数组。

这个想法是在用户单击任务日志的多个嵌套层时显示随后选择的任务的层次结构。

这些事件在类型 'ContextObject' 中定义如下:

    hierarchy: Event[] | undefined
    setHierarchy: Dispatch<SetStateAction<Event[] | undefined>>
    selectedSubEvent: Event[];
    setSelectedSubEvent: Dispatch<SetStateAction<Event[]>>;

它们通过 useState 进行管理:

    const [hierarchy, setHierarchy] = useState<Event[] | undefined>([]);
    const [selectedSubEvent, setSelectedSubEvent] = useState<Event[]>([]);

为了使用所选任务更新层次结构,我在 useEffect 中使用状态更新函数,如下所示,并在“...prevState”上收到错误:

    useEffect(() => { 
        setHierarchy((prevState) => [...prevState, selectedSubEvent])
    }, [selectedSubEvent]);

我遇到的错误:

    Type 'Event[] | undefined' is not an array type.ts(2461)
    (parameter) prevState: Event[] | undefined

当我放弃 useEffect 中的状态更新功能而只是将 'Hierarchy' 设置为 'selectedEvent' 时,它确实将该单个事件设置为层次结构,但显然我需要的不止于此。

我也试过在传播它之前检查 prevState,但是出现了多个错误:

    setHierarchy((prevState) =>
        prevState ? [...prevState, selectedSubEvent] : [selectedSubEvent]
        );

//Errors
Argument of type '(prevState: Event[] | undefined) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[] | undefined>'.
  Type '(prevState: Event[] | undefined) => (Event | Event[])[]' is not assignable to type '(prevState: Event[] | undefined) => Event[] | undefined'.
    Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
      Type 'Event | Event[]' is not assignable to type 'Event'.
        Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)

我意识到我没有提供太多我尝试过的方法,但我尝试了无数文章、教程以及 React 和 TypeScript 文档,但我不知所措 ‍♂️

如果我能提供更多信息,请告诉我。如果有人能给我指明正确的方向,我很乐意继续挖掘。


更新

我尝试了@Leigh.D提供的建议,得到了以下结果。

首先: 删除 'undefined' 会出现以下错误:

Argument of type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[]>'.
  Type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to type '(prevState: Event[]) => Event[]'.
    Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
      Type 'Event | Event[]' is not assignable to type 'Event'.
        Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)
(parameter) prevState: Event[]

第二个: 这也会导致我的上下文提供程序值中的 setHierarchy 属性 出错,但是当我从上下文对象中的 setHierarchy 中删除 'undefined' 时,如下所示:

setHierarchy: Dispatch<SetStateAction<Event[]>>

此错误消失:

Type 'Dispatch<SetStateAction<Event[]>>' is not assignable to type 'Dispatch<SetStateAction<Event[] | undefined>>'.
  Type 'SetStateAction<Event[] | undefined>' is not assignable to type 'SetStateAction<Event[]>'.
    Type 'undefined' is not assignable to type 'SetStateAction<Event[]>'.ts(2322)
sub-event-context.tsx(20, 2): The expected type comes from property 'setHierarchy' which is declared here on type 'SubEventContextObject'
(property) setHierarchy: React.Dispatch<React.SetStateAction<Event[] | undefined>>

第三: 当我按照 FIRST 组合删除 'undefined' 并检查 'prevState' 时,我得到以下信息:

Argument of type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[]>'.
  Type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to type '(prevState: Event[]) => Event[]'.
    Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
      Type 'Event | Event[]' is not assignable to type 'Event'.
        Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)
(parameter) prevState: Event[]

第四: 当我删除选中的 'prevState' 并对其进行类型转换时,VSCode/Prettier 将其括在括号中——我不知道这是否是正常行为?

useEffect(() => {
        setHierarchy((prevState) => [
            ...(prevState as Event[]),
            selectedSubEvent,
        ]);
    }, [selectedSubEvent]);

除此之外,我得到了与 THIRD 相同的错误。

解决方案

感谢 Leigh.D 和 Daniel Baldi 帮助我解决了这个问题。看来我有两个问题,他们的两个答案共同解决了我的错误。阅读本文并希望解决类似问题的任何人,请务必阅读这两个答案。

我将 Daniel 的解决方案标记为已接受的答案,主要是因为在我缺乏经验的情况下,这是一个更复杂的问题。如果我的想法不正确,请指正。

再次感谢您提供的帮助。我很感激!

迪恩

问题出在您的类型定义(特别是未定义)以及扩展运算符如何解释它。

const [hierarchy, setHierarchy] = useState<Event[] | undefined>([]);

您可以从类型中删除 undefined(因为您无论如何都将状态默认为空数组),在设置状态或类型转换 prevState 时执行检查,以便传播运算符知道它是一个数组(不是最佳选择)即:

setHierarchy((prevState) => prevState? [...prevState, selectedSubEvent] : [selectedSubEvent])

setHierarchy((prevState) => [...prevState as Event[], selectedSubEvent])

好的,所以我想我已经弄清楚这里发生了什么。让我们先考虑一下这里发生了什么:

setHierarchy((prevState) =>
    prevState ? [...prevState, selectedSubEvent] : [selectedSubEvent]
    );

正如您在 ContextObject 界面中所述,selectedSubEventEvent 的数组,并且 prevState 具有相同类型的 hierarchy 并且因此是 Event 或未定义的数组。

因此查看代码,如果 prevState 的计算结果为 truthy 值,则您正在创建此 [...prevState, selectedSubEvent],其中包含 [= =15=],结果为 EventselectedSubEvent,结果为 事件数组 。这是第一个问题,因为结果值最终是 (Event | Event[])[] 类型,而它应该是 Event[].

类型

prevStatefalsy 时,您将 hierarchy 设置为 [selectedSubEvent]。这将创建事件 (Event[][]) 的 数组数组 。这是第二个问题!

我可能在这里弄错了,因为在没有 运行 的情况下尝试编写代码是非常棘手的,但我的猜测是尝试这个,它应该工作正常:

setHierarchy((prevState) =>
    prevState ? [...prevState, ...selectedSubEvent] : selectedSubEvent
    );