将 TypeScript class 移到自定义挂钩之外

Moving TypeScript class outside of custom hook

我是打字稿的新手,几个小时以来我一直在思考一个问题。

为了简单起见,场景是我有一个接收一些参数的反应自定义钩子,在钩子的主体中我定义了一个 class 在其构造函数中我使用传递给钩子的参数.

万一它是相关的,在构造函数中我还使用从钩子的参数派生的变量(例如:钩子接收一个 id,然后找到相应的 DOM 元素和最后,该元素在 class 的构造函数中使用;它没有作为参数传递给构造函数,只是因为它已经存在于钩子的范围内而被使用。

现在的问题是将 class 的声明移到挂钩之外(因为我为挂钩声明了大量类型,并且我正在将它们全部移动到另一个文件中)。

我在 Google 的几个小时内找到的唯一可能可行的方法是将参数添加到与钩子内部可用变量相对应的构造函数,然后在挂钩 bind the constructor 中使用可用变量(在创建 class 的实例时直接传递参数将意味着有冗余代码重复,也因为这些变量实际上是常量)。

我想这个问题很常见,大多数 TypeScript 开发人员一生中至少要面对并解决一次,所以我想知道这里有哪些选择?我必须去绑定到构造函数吗?还有哪些其他方法可行?

编辑:

下面有一些代码应该可以很好地重现我的钩子的逻辑。

import { useEffect, useRef } from 'react';

type SomeFancyType = { a: string; b: boolean };

const defaultConfig = { a: 'default', b: true } as SomeFancyType;

function MyCustomHook(id: string, anotherId: string, configObject: SomeFancyType) {
  const elementRef = useRef<HTMLElement | null>(null);

  class ThatsMyIssue {
    element: HTMLElement | null;

    constructor(id = '') {
      const candidate = document.getElementById(id) as HTMLElement | null;
      this.element = elementRef.current!.contains(candidate) // && another check involving `configObject`
        ? candidate
        : null;
    }

    triggerClick() {
      this.element?.click();
    }
  }

  useEffect(() => {
    elementRef.current = document.getElementById(id);
    if (elementRef.current) 'all good';
  }, []);

  useEffect(() => {
    const element = new ThatsMyIssue(anotherId);
    if ('some condition') element.triggerClick();
    const anotherElement = new ThatsMyIssue('sourceNotEasyToReproduce');
    if (anotherElement) 'very good';
  }, [elementRef.current]);
}

export default MyCustomHook;

我需要把 class ThatsMyIssue 移到挂钩外面。

问题是 class 构造函数正在使用 elementRef.currentconfigObject,它们作为挂钩的参数提供。

我找到的解决方案是修改构造函数以接受这些变量作为参数:

constructor(id = '', elementRef: HTMLElement | null, configObject: SomeFancyType) { ... }

然后在钩子中将变量绑定到构造函数,这样我就可以通过仅提供第一个参数(这是唯一实际更改的参数)来创建实例:

const BindedConstructor = ThatsMyIssue.bind(null, elementRef.current, configObject || defaultConfig);

const element = new BindedConstructor(anotherId);
const anotherElement = new BindedConstructor('sourceNotEasyToReproduce');

但我想知道是否有其他方法可以解决这个问题。

我刚开始使用 TypeScript,我不想自学如何使事情过于复杂,而 JavaScript 社区可能正在为这种情况使用一些经过深思熟虑的模式。

嗯,是的,如果你想使用来自不同范围的值,你必须传递它们。

但与其绑定有点复杂和奇怪的构造函数,我可能只是声明一个知道如何实例化这些对象的函数,然后调用该函数。

function MyCustomHook(id: string, anotherId: string, configObject: SomeFancyType) {
  const elementRef = useRef<HTMLElement | null>(null);

  function makeThing(id: string): ThatsMyIssue {
    return new ThatsMyIssue(id, elementRef)
  }

  useEffect(() => {
    const element = makeThing(anotherId);
    if ('some condition') element.triggerClick();
    const anotherElement = makeThing('sourceNotEasyToReproduce');
    if (anotherElement) 'very good';
  }, [elementRef.current]);
}

Playground