使用动态导入加载 React 钩子?

Leo*_*ang 11 javascript reactjs webpack react-hooks

我正在使用一些初始渲染不需要的第三方 React 钩子库。例如react-use-gesturereact-spring、 和react-hook-form。它们都提供交互性,可以等到 UI 呈现后。我想import()在渲染我的组件后使用 Webpack 的代码拆分(即)动态加载这些。

但是,我无法删除 React 钩子,因为它本质上是一个条件钩子,React 不支持它。

我能想到的2个解决方案是:

  1. 以某种方式将钩子提取到组件中并使用组合
  2. 强制 React 在钩子加载后重建组件

这两种解决方案看起来都很老套,未来的工程师很可能会把它搞砸。有没有更好的解决方案?

Dmi*_*try 8

正如您所说,有两种方法可以使用延迟加载的钩子:

  1. 在父组件中加载库,在可用时使用库有条件地渲染组件

类似的东西

let lib
const loadLib = () => {...}

const Component = () => {
  const {...hooks} = lib
  ...
}

const Parent = () => {
  const [loaded, setLoaded] = useState(false)
  useEffect(() => loadComponent().then(() => setLoaded(true)), [])
  return loaded && <Component/>
}
Run Code Online (Sandbox Code Playgroud)

这种方法确实有点hacky,每个库都需要大量的手动工作

  1. 使用钩子开始加载组件,失败,加载钩子时重建组件

这可以在React.Suspense的帮助下简化

<Suspense fallback={"Loading..."}>
  <ComponentWithLazyHook/>
</Suspense>
Run Code Online (Sandbox Code Playgroud)

Suspense 的工作原理类似于 Error Boundary,如下所示:

  1. 组件在渲染期间抛出一个 Promise(通过React.lazy或手动)
  2. Suspense 捕获 Promise 并呈现 Fallback
  3. 承诺解决
  4. Suspense 重新渲染组件

Suspense for Data Fetching从实验阶段成熟时,这种方式可能会更受欢迎。

但是对于我们一次加载库并可能缓存结果的目的,数据获取的简单实现可以做到这一点

const cache = {}
const errorsCache = {}
// <Suspense> catches the thrown promise
// and rerenders children when promise resolves
export const useSuspense = (importPromise, cacheKey) => {
  const cachedModule = cache[cacheKey]
  // already loaded previously
  if (cachedModule) return cachedModule

  //prevents import() loop on failed imports
  if (errorsCache[cacheKey]) throw errorsCache[cacheKey]

  // gets caught by Suspense
  throw importPromise
    .then((mod) => (cache[cacheKey] = mod))
    .catch((err) => {
      errorsCache[cacheKey] = err
    })
};

const SuspendedComp = () => {
  const { useForm } = useSuspense(import("react-hook-form"), "react-hook-form")
  const { register, handleSubmit, watch, errors } = useForm()
  ...
}

...

<Suspense fallback={null}>
  <SuspendedComp/>
</Suspense>
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看示例实现。

编辑:

当我在 codeandbox 中编写示例时,我完全没有意识到依赖项解析的行为与 webpack 中的本地不同。

Webpackimport() 无法处理完全动态的路径,例如import(importPath). 它必须在import('react-hook-form')某个地方静态地在构建时创建一个块。

所以我们必须import('react-hook-form')自己编写并提供importPath = 'react-hook-form'用作缓存键的 。

我将 codesanbox 示例更新为可与 webpack 一起使用的示例,旧示例在本地无法使用,可以在此处找到