javascript 动态导入react钩子

nr7wwzry  于 2023-01-04  发布在  Java
关注(0)|答案(4)|浏览(168)

我们可以根据传递给组件的值动态导入钩子吗?
例如
App.js

<BaseComponent isActive />

BaseComponent.js

if(props.isActive) {
  // import useActive (custom Hook)
}

我不希望这些(自定义挂钩)文件被导入,并增加基本组件的大小,即使 prop 包含虚假的值。

moiiocjp

moiiocjp1#

你可以动态导入钩子,因为它只是一个函数(使用require),但是你不应该因为你不能在条件中使用钩子
参见弯钩规则
仅在顶级调用钩子**。不要在循环、条件或嵌套函数内调用钩子。
如果你想有条件地使用一个钩子,在它的实现中使用条件(例如,从Apollo GraphQL客户端查看useQuery钩子的skip选项)。

const useActive = (isUsed) => {
  if (isUsed) {
    // logic
  }
}
apeeds0o

apeeds0o2#

您应该提取useActive钩子内部的逻辑并动态导入它,而不是动态导入钩子,因为您不应该在循环、条件或嵌套函数内部调用钩子。,checkout Rules of Hooks
假设您的useActive钩子试图更新文档标题(在真实的世界中,它必须是一大块代码,您可以考虑使用动态导入)
它可以按如下方式实现:

// useActive.js

import { useEffect } from "react";    
export function useActive() {
  useEffect(() => {
    document.title = "(Active) Hello World!";
  }, []);
}

您尝试在BaseComponent中使用它:

// BaseComponent.js

function BaseComponent({ isActive }) {
  if (isActive) { // <-- call Hooks inside conditions ❌
    import("./useActive").then(({ useActive }) => {
      useActive();
    });
  }
  return <p>Base</p>;
}

这里您违反了“不要在条件内调用钩子”的规则,并将得到Invalid hook call.错误。
因此,您可以提取钩子内部的逻辑并动态导入它,而不是动态导入钩子:

// updateTitle.js

export function updateTitle() {
  document.title = "(Active) Hello World!"
}

在钩子内部执行isActive检查:

// BaseComponent.js

function BaseComponent({ isActive }) {
  useEffect(() => {
    if (!isActive) return;

    import("./updateTitle").then(({ updateTitle }) => {
      updateTitle();
    });
  }, [isActive]);

  return <p>Base</p>;
}

它工作正常,没有违反任何规则的挂钩。
我附加了一个CodeSandbox供您使用:

rsl1atfo

rsl1atfo3#

无论谁遇到它:您不能在动态导入的组件中使用Hooks(但是,如果您不使用Hooks,即使第一个示例也能正常工作):
而不是:

const useDynamicDemoImport = (name) => {
  const [comp, setComp] = useState(null);
  useEffect(() => {
    let resolvedComp = false;
    import(`@site/src/demos/${name}`)
      .then((m) => {
        if (!resolvedComp) {
          resolvedComp = true;
          setComp(m.default);
        }
      })
      .catch(console.error);
    return () => {
      resolvedComp = true;
    };
  }, []);
  return comp;
};

const DemoPreviewer: FC<DemoPreviewerProps> = (props) => {
  comp = useDynamicDemoImport(props.name);
  return (
    <Paper sx={{ position: "relative" }}>
      {comp}
    </Paper>
  );
};

export default DemoPreviewer

请改用ReactLazy,稍后再呈现组件

const useDynamicDemoImport = (name) => {
  const Comp = React.lazy(() => import(`@site/src/demos/${name}`));
  return comp;
};

const RootDemoPreviewer: FC<DemoPreviewerProps> = (props) => {
  console.log("RootDemoPreviewer");
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <DemoPreviewer {...props} />
    </React.Suspense>
  );
};

const DemoPreviewer: FC<DemoPreviewerProps> = (props) => {
  const Comp = useDynamicDemoImport(props.name);
  return (
    <Paper sx={{ position: "relative" }}>
      <Comp />
    </Paper>
  );
};

export default RootDemoPreviewer
b91juud3

b91juud34#

我同意其他人的回答,你不应该这么做。但是如果你必须这么做,你可以创建一个更高级别的组件来获取钩子,然后把它作为一个 prop 传递给一个被 Package 的组件。通过这样做,被 Package 的组件可以在不违反钩子规则的情况下使用钩子,例如从被 Package 组件的Angular 来看,对钩子的引用永远不会改变,并且每次被 Package 的组件呈现时都会被调用。2代码如下所示:

export function withDynamicHook(hookName, importFunc, Component) {
    return (props) => {
        const [hook, setHook] = useState();

        useEffect(() => {
            importFunc().then((mod) => setHook(() => mod[hookName]));
        }, []);

        if (!hook) {
            return null;
        }

        const newProps = { ...props, [hookName]: hook };
        return <Component {...newProps} />;
    };
}

// example of a Component using it:
const MyComponent = ({useMyHook}) => {
    let something = useMyHook();
    console.log(something)
    return <div>myHook returned something, see the console to inspect it </div>
}
const MyComponentWithHook = withDynamicHook('useMyHook', () => import('module-containing-usemyhook'), MyComponent)

相关问题