Reputation: 79
Code:
React version:
"dependencies": {
"react": "^18.1.0",
"react-dom": "^18.1.0",
},
"devDependencies": {
"@types/node": "^17.0.30",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
"typescript": "^4.6.4",
}
I'm trying to wrap the lazy component in <React.Suspense> by a HOC to reduce redundant code, but I get a TS Error:
Type 'P' is not assignable to type 'IntrinsicAttributes & (T extends MemoExoticComponent<infer U> | LazyExoticComponent<infer U> ? ReactManagedAttributes<U, ComponentPropsWithRef<T>> : ReactManagedAttributes<...>)'
My HOC code is here:
function lazyLoading<P = {}, T extends ComponentType<P> = ComponentType<P>>(LazyComponent: LazyExoticComponent<T>) {
return (props: P): JSX.Element => (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent {...props} />
</Suspense>
)
}
A simpler version without considering about props:
function lazyLoadingNoProps<T extends ComponentType<any>>(LazyComponent: LazyExoticComponent<T>) {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)
}
Since I don't want to use LazyExoticComponent<any>
(to get rid of any
), I decided to let TS infer the type of lazy component for me.
I've had a look at React.lazy
's signature which works perfectly well, I decided to use it on my HOC
export function lazy<T extends ComponentType<any>>(
factory: () => Promise<{default: T}>
): LazyExoticComponent<T>
And I get the following TS error for this simpler version:
Type '{}' is not assignable to type 'T extends MemoExoticComponent<infer U> | LazyExoticComponent<infer U> ? ReactManagedAttributes<U, ComponentPropsWithRef<T>> : ReactManagedAttributes<...>'
I've totally no idea what's happening here, as the Application is working as expected at runtime, but a TS error here in IDE.
Usage Example (simpler version without props):
const Admin = lazyLoading(lazy(() => import('./pages/Admin')))
const App = (): JSX.Element => (
<Routes>
<Route
path="/admin"
element={Admin}
/>
{/* ... */}
)
HOC code is here: Link to TypeScript Playground
Upvotes: 2
Views: 2020
Reputation: 120
export function withSuspense<P extends JSX.IntrinsicAttributes>(
Component: React.ComponentType,
suspenseProps: SuspenseProps & {
FallbackComponent?: ComponentType;
},
) {
return function WithSuspense(props: P) {
return (
<Suspense
fallback={
suspenseProps.fallback ||
(suspenseProps.FallbackComponent && createElement(suspenseProps.FallbackComponent))
}
>
<Component {...props} />
</Suspense>
);
};
}
Upvotes: -1
Reputation: 11
Here's how to achieve it in TypeScript
import { Loader } from "../components/loader";
import { Suspense } from "react";
/**
* HOC to wrap a component in a Suspense component.
*/
export default function withSuspense<P>(Component: React.ComponentType & any) {
return function WithSuspense(props: P) {
return (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
};
}
Upvotes: 0