I'm trying to write a hook which performs a dynamic import
and returns a Promise
which resolves when the import has completed.
Here's how I envision it's usage:
await useAfterImport("@custom/path");
Here is my faulty attempt:
const useAfterImport = (importPath:string) => {
return new Promise<void>((resolve) => {
useEffect(() => {
import(importPath).then(() => {
resolve();
});
}, [importPath]);
});
};
This attempt fails because the Rules of Hooks have been violated:
React Hook "useEffect" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function. eslintreact-hooks/rules-of-hooks
Is there a way that a hook like this can be written without resorting to using a callback parameter?
I would really like to be able to return a Promise
so it can be await
ed.
I don't think Promises are the right way to go here:
Here's how I envision it's usage:
await useAfterCustomImport("@custom/path");
The top level of React component function bodies - the only place hooks can be used - can't have await
inside of them - they need to render something immediately (even if temporarily an empty fragment).
For what you're trying to do, state makes more sense. You could do something like the following:
const useAfterCustomImport = (importPath: string) => {
const [imported, setImported] = useState(false);
useEffect(() => {
import(importPath)
.then(() => setImported(true))
// .catch(handleErrors); // don't forget this part
}, []);
return imported;
};
and then
const libImported = useAfterCustomImport('some-lib');
return !libImported ? null : (
<div>
// JSX that uses the library
</div>
);
This follows the current logic of your current code, which appears to assume that the import contains only side-effects, rather than resolving to useable values. If the module resolves to a useable value, returning both imported
and the resolve value from the custom hook would be a trivial tweak.