使用动态导入加载React挂钩



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

然而,我不能截断React钩子,因为它本质上是一个条件钩子,React不支持它。

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

  1. 以某种方式将钩子提取到组件中并使用composition
  2. Force React在吊钩负载后重建部件

这两种解决方案似乎都很糟糕,未来的工程师很可能会把它搞砸。有更好的解决方案吗?

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

  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/>
}

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

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

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

<Suspense fallback={"Loading..."}>
<ComponentWithLazyHook/>
</Suspense>

暂停工作类似于错误边界,如下所示:

  1. 组件在渲染过程中抛出Promise(通过React.layen或手动(
  2. Suspense捕捉到Promise并渲染Fallback
  3. 承诺解决
  4. Suspense重新渲染组件

当数据获取暂停从实验阶段成熟时,这种方式可能会变得更受欢迎。

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

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>

您可以在这里看到一个示例实现。

编辑:

当我在codesandbox中写这个例子时,我完全没有意识到依赖解析的行为将与webpack中的本地行为不同。

Webpackimport()不能像import(importPath)那样处理完全动态的路径。它必须在某个静态位置具有import('react-hook-form'),以便在构建时创建块。

因此,我们必须自己编写import('react-hook-form'),并提供importPath = 'react-hook-form'作为缓存密钥。

我将codesanbox示例更新为可与webpack一起使用的示例,旧示例在本地不起作用,可以在中找到

您考虑过截尾钩子吗?我们使用了类似于异步加载大型lib的东西,但它不是钩子,所以YMMV。

// init with stub
let _useDrag = () => undefined;
// load the actual implementation asynchronously
import('react-use-gesture').then(({useDrag}) => _useDrag = useDrag);
export asyncUseDrag = (cb) => _useDrag(cb)

相关内容

  • 没有找到相关文章

最新更新