TypeScript 自定义 useStateIfMounted React Hook - 并非类型 'T | 的所有组成部分((value: SetStateAction<T>) => void)' 可调用

问题描述 投票:0回答:2

完整错误:

Not all constituents of type 'T | ((value: SetStateAction<T>) => void)' are callable.
Type 'T' has no call signatures.

总结:

我正在尝试创建一个

useStateIfMounted
自定义挂钩。它工作得很好,但我似乎无法为 TypeScript 正确输入它。钩子很好,但是调用者在尝试调用 setState() 时收到错误。我应该如何输入 useCallback() 才能获得 TypeScript 良好的输入系统。 args 应该与 React 中的常规 setState 完全相同。

我的代码:

/**
 * Same as useState, but the setState will not run if component is unmounted.
 * 
 * The setState is safe to use in dependency arrays because useCallback() has empty dependencies.
 */
export function useStateIfMounted<T>(initialState: T | (() => T)) {
    const componentUnmountedRef = useRef(false);
    const [state, _setState] = useState<T>(initialState);

    // TEMP: Line below will result in error if we don't type as 'any'
    const setState: any = useCallback((value: SetStateAction<T>) => {
        if (!componentUnmountedRef?.current) {
            _setState(value);
        }
    }, [])

    useEffect(() => {
        return () => {
            componentUnmountedRef.current = true;
        }
    }, [])

    return [state, setState]
}

然后,当用户尝试调用

setState()
时,TypeScript 将显示错误。我能解决这个问题的唯一方法是暂时将回调 (useCallback) 设置为“任意”,但这并不能让我很好地输入允许作为参数输入的内容。请参阅下面的示例了解错误是如何发生的:

---REDACTED---
const [someState, setSomeState] = useState<CustomType>(null)
---REDACTED---

setSomeState(SomeCustomObject) // <-- This line displays TypeScript errors

我尝试过的:

  1. const setState = useCallback((value: SetStateAction<T>) => { ... }
  2. const setState: Dispatch<SetStateAction<T>> = useCallback((value: SetStateAction<T>) => { ... }
  3. const setState = useCallback((value: T | ((prevState: T) => T) => { ... }
  4. const setState = useCallback((value: any) => { ... }
    <-- This one is curious as I would have expected TypeScript to allow me to pass 任何争论。

免责声明:所有这些都是我自己的代码,但名称是我从this repo得到的。他们没有用 TypeScript 编写代码,所以我要求正确输入代码。我原来的名字是

useStateUnmountedAsync
,但我更喜欢他们的名字。我发现了几个其他自定义钩子,它们本质上是相同的想法,但没有一个是用 TypeScript 编写的。

编辑:添加我的 tsconfig.json 和版本 tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "src",
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "downlevelIteration": true,
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "allowUnreachableCode": true
  },
  "include": [
    "src"
  ],
  "exclude": [
    "**/node_modules/**",
    "build/**",
    "public/**"
  ]
}

版本:

"react": "^17.0.1",
"react-scripts": "4.0.3",
"typescript": "^4.2.3",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
reactjs typescript react-hooks
2个回答
5
投票

以元组形式输入返回值:

return [state, setState] as const;

您需要指定这一点,否则

state
setState
都是
boolean | ((value: React.SetStateAction<boolean>) => void)
类型,并且该联合中只有一部分是可调用的。元组确保状态位于索引 0,setter 位于索引 1。

这本质上是多余的,但要获得与

useState
相同的类型,您还可以考虑将
setState
显式键入为:

const setState: React.Dispatch<SetStateAction<T>>

0
投票

明确指定 setter 函数的类型以及钩子的返回类型。

type SetValue<T> = Dispatch<SetStateAction<T>>;

export function useStateIfMounted<T>(initialState: T | (() => T)): [T, SetValue<T>]  {
 // code here

 
 const setState: SetValue<T> = useCallback((value) => {
   // code here
}

// code here
}
© www.soinside.com 2019 - 2024. All rights reserved.