【问题标题】:React hooks dependency array vs. TypeScript discriminated unionsReact hooks 依赖数组与 TypeScript 区分联合
【发布时间】:2020-01-17 19:57:13
【问题描述】:

我的 React+TypeScript 应用程序有一个组件,其 props 类型是可区​​分的联合。这有助于防止在编译时无效的道具组合。但是,如果我需要将其中一些道具传递到 React Hook 的依赖列表中,那么正确的方法是什么?钩子规则说我不能在条件中调用钩子,但是如果它们 not 在条件(又名类型保护)内,区分的道具将导致 TS 编译器错误。

我考虑过以下变通方法,所有这些似乎都会降低我的代码的可读性和/或安全性:

  1. 拆分为多个组件并将挂钩推送到每个实现中。这会重复很多代码。
  2. 将每个已识别的 prop 提取到局部变量中,例如const values = p.isMulti === true && p.values 并在依赖项列表中使用它。除了看起来不必要的冗长之外,这种方法还可以让以后的代码绕过 TypeScript 的类型检查。
  3. 在依赖列表中,将普通属性访问(例如p.value)替换为类型安全的表达式,例如p.isMulti === true && p.values。这里的问题是 React 的穷举依赖 ESLint 规则抱怨(“React Hook React.useMemo 在依赖数组中有一个复杂的表达式。将它提取到一个单独的变量中,以便对其进行静态检查。”)因为这些表达式不是静态可测试的。我必须禁用我想避免的穷举规则,因为以后的问题不会被 ESLint 捕获。
  4. 在依赖列表中添加类型转换以消除 TS 错误。这会触发来自 ESLint 的详尽的 deps 投诉,就像上面的类型保护表达式一样。
  5. 更改可区分联合以使所有道具都显示在两个分支中,但要求“错误”分支的值始终为readonly 和undefined。这将解决钩子问题,但会降低组件的类型安全性。
  6. 重构组件以删除鉴别器道具,而是将联合化下推到每个道具中。这在下面的人为示例中很容易(例如,单个 values 道具可以是 string 或 string[]),但在我的真实应用程序中,联合要复杂得多,所以这个“合并道具”选项会使组件更难正确使用。
  7. 对 deps 列表中的 props 应用类型转换,例如(p.value as string | undefined。这似乎是目前最不令人反感的选择。

还有更好的选择吗?

这是一个说明问题的人为示例:https://codesandbox.io/s/interesting-tereshkova-4siy4。代码重复如下。

import * as React from 'react';
import { render } from 'react-dom';

type Props =
  | {
      isMulti: true;
      values: string[];
    }
  | {
      isMulti: false;
      value: string;
    };

const X = (p: Props) => {
  return React.useMemo(
    () => (
      <div>
        value(s):
        {p.isMulti === true && p.values.join(',')}
        {p.isMulti === false && p.value}
      </div>
    ),
    [p.isMulti, p.values, p.value], // TS errors on p.value/p.values
  );
};

function App() {
  return (
    <div className="App">
      <X isMulti={false} value={'foo'} />
      <X isMulti={true} values={['foo', 'bar', 'baz']} />
    </div>
  );
}

const rootElement = document.getElementById('root');
render(<App />, rootElement);

【问题讨论】:

  • 根据我的回答,选项 (5) 是您最好的选择。我还认为它本质上并不比其他任何东西都不安全。如果不检查您的判别属性,您无论如何都不会知道哪个是正确的属性,这与typeof 或instanceof 检查没有什么不同。由于您需要 useMemo 的所有属性,因此您别无选择。

标签: reactjs typescript react-hooks


【解决方案1】:

typescript 在这里看到的问题是 p.values 和 p.value 在您的类型定义中是互斥的。相反,请考虑以下类型:

type Props =
  | {
      isMulti: true;
      value: undefined
      values: string[];
    }
  | {
      isMulti: false;
      value: string;
      values: undefined
    };

像这样,TS 会推断出值/值始终以它们的类型或未定义的形式存在(这是无论如何都不存在的属性的默认值),而不是互斥的。例如,如果 isMulti 为真,则 TS 还会正确推断他们的类型,例如,value 将是 undefined。

另一种选择是放弃整个价值/价值方面:

type Props =
  | {
      isMulti: true;
      value: string[]
    }
  | {
      isMulti: false;
      value: string;
    };

在这种情况下,您可以只使用value,而不是使用两个名称相似的属性。

【讨论】:

    猜你喜欢
    • 2020-04-03
    • 2020-04-26
    • 2019-10-17
    • 2020-06-18
    • 2021-05-23
    • 1970-01-01
    • 2021-10-21
    • 2020-06-09
    • 2021-10-31
    相关资源
    最近更新 更多