Reputation: 17239
I'm trying to make a simple blog.
What I want to do is conditionally import a specific component based on the url params (id below).
However this code only renders Loading, it never changes. Why is this?
import Layout from "../../components/Layout";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
const LoadingPost = () => {
return <h1>Loading</h1>;
};
const Post = () => {
const router = useRouter();
const { id } = router.query;
const [loaded, setLoaded] = useState(false);
let PostToShow = LoadingPost;
useEffect(() => {
if (id) {
import(`../../posts/${id}.tsx`).then(_ => {
PostToShow = () => _;
setLoaded(true);
});
}
}, [id]);
const renderPost = () => {
if (loaded) {
return <PostToShow />;
}
};
return (
<Layout>
<h1>This would be a post</h1>
<h2>The id of this post would be: {id}</h2>
{renderPost()}
</Layout>
);
};
export default Post;
Upvotes: 0
Views: 38
Reputation: 17239
The issue here is that we're importing a module, not just a component.
import Layout from "../../components/Layout";
import { useEffect, useState } from "react";
const LoadingPost = () => {
return <h1>Loading</h1>;
};
const Post = ({ pageProps }) => {
const { id } = pageProps;
const [loaded, setLoaded] = useState(false);
const [Component, setComponent] = useState(LoadingPost);
useEffect(() => {
if (id) {
import(`../../posts/${id}.tsx`).then(_ => {
setComponent(_);
setLoaded(true);
});
}
}, [id]);
const renderPost = () => {
if (loaded) {
// @ts-ignore
const Comp = Component.default;
// @ts-ignore
return <Comp />;
}
};
return (
<Layout>
<h1>This would be a post</h1>
<h2>The id of this post would be: {id}</h2>
{renderPost()}
</Layout>
);
};
Post.getInitialProps = ctx => {
const { id } = ctx.query;
return {
pageProps: {
id,
},
};
};
export default Post;
As other people mentioned, we can do setComponent(_.default)
either, but I couldn't get that working as it gives: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
.
The above works as intended:
Upvotes: 0
Reputation: 61
You're doing the following:
import(`../../posts/${id}.tsx`).then(_ => {
PostToShow = () => _;
setLoaded(true);
});
So you're changing the value for the PostToShow
variable, then setting the state - which triggers a new render. However on ever render you do:
let PostToShow = LoadingPost;
Hence you always render the LoadingPost
component.
Upvotes: 0
Reputation: 22875
Here is how you can do this
import(`../../posts/${id}.tsx`).then(module=> {
PostToShow = module.default;
setLoaded(true);
});
Upvotes: 1