-
-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add ClientOnly component #35
Conversation
Hi, @brillout! This is the PR for adding ClientOnly Component. Also, since I am new to contributing to OSS, I would like to know if there are any deficiencies regarding the creation of PR. |
If I've misunderstood the scope of the issue and it involves more extensive work, or if it's an issue that I should not be working on, I would appreciate it if you could let me know. |
Very neat 💯 @AurelienLourot WDYT? @usk94 Welcome to OSS contributing 🦾 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great @usk94, thanks a lot! If you feel like it, another PR with an example would be also welcome
Also if you feel like doing the same for Vue and Solid, very welcome too |
I wonder if something like the following would be possible? <ClientOnly
fallback={<Loading />}
component={() => import('../star-wars/index/+Page')}
element={(Page) =>
<Page
movies={[
{
id: '1',
title: 'foo',
release_date: 'today'
}
]}
/>
}
/> Or maybe even: <ClientOnly fallback={<Loading />}>{
async () => {
const Page = await import('../star-wars/index/+Page')
return (
<Page
movies={[
{
id: '1',
title: 'foo',
release_date: 'today'
}
]}
/>
)
}
}</ClientOnly> WDYT? The thing I like about this is that it's syntactically closer to "regular React", but feel free to disagree. |
@brillout @AurelienLourot How about this fix? ebbcafd This allows the ClientOnly component to be used in this way. <ClientOnly
fallback={<Loading />}
component={() => import('../star-wars/index/+Page')}
componentRenderer={(Page) => (
<Page
movies={[
{
id: '1',
title: 'foo',
release_date: 'today'
}
]}
/>
)}
/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
LGTM 👌 Just one little nitpick: the <ClientOnly
fallback={<Loading />}
component={() => import('../star-wars/index/+Page')}
>{
(Page) => (
<Page
movies={[
{
id: '1',
title: 'foo',
release_date: 'today'
}
]}
/>
)
}</ClientOnly> Thoughts? As always: feel free to disagree 🙂 |
Thanks for the feedback! I agree that it's a bit verbose. Personally, I prefer the option of simply renaming 'componentRenderer' to 'render'. However, I'm also open to the idea of using children as a function😀 |
I was also hesitating between the two and I also thought about the name "render" 😀 And I also had a preference for "render" at first, as I ain't a fan of passing a function as But, after some thoughts, I did end up prefering children as a function because I've a slight preference for this: <GrandGrandParent>
<GrandParent>
<Parent>
<ClientOnly fallback={<Loading />} load={() => import('../star-wars/index/+Page')}>{
(Page) => (
<Page movies={movies}>
<Child>
<GrandChild>
<Leaf />
</GrandChild>
</Child>
</Page>
)
}</ClientOnly>
</Parent>
</GrandParent>
</GrandGrandParent> Over this: <GrandGrandParent>
<GrandParent>
<Parent>
<ClientOnly
fallback={<Loading />}
component={() => import('../star-wars/index/+Page')}
render={
(Page) => (
<Page movies={movies}>
<Child>
<GrandChild>
<Leaf />
</GrandChild>
</Child>
</Page>
)
}
/>
</Parent>
</GrandParent>
</GrandGrandParent> Thoughts? FYI I was also hesitating of whether we should rename Edit: renamed |
Actually, what do you think of this? I feel like it could be slightly clearer, as it's a neat break down in two parts: the loading and then the rendering. Which is exactly what is happening: lazy-loading and conditional rendering. |
@brillout Also, I've switched to the |
Neat 💯 I've made some minor changes, see commits. Feel free to object to any change I've made. I've a question: since we pass two functions to the dependency array |
I think the solution is to set the dependency array to the emtpy array |
@brillout
Indeed, as the component is primarily used for handling heavy data fetches, it's unlikely that the children's props will change frequently. So I have updated it to use an empty array👌 |
Ah, good point, it isn't only the component that is blocked but also any prop passed below the If the user wants to pass props down through the It actually seems quite a frequent use case. Example: function MapWrapper(props) {
const { geoLocation } = props
// We need to use a key for geoLocation changes to propagate through <ClientOnly>
return (
<ClientOnly
fallback={<Loading />}
key={JSON.stringify(geoLocation)}
load={() => import('heavy-map-library')}
>{
(Map) => <Map geoLocation={geoLocation} />
}</ClientOnly>
)
} I wonder if there is an alternative that doesn't require |
@brillout Since The rendering of the component is dynamic like this. 2023-11-24.1.44.33.movfunction Page() {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount((prev) => prev + 1)}>set!</button>
<ClientOnly fallback={<Loading />} refreshKey={count} load={() => import('../star-wars/index/+Page')}>
{(Page) => <Page movies={[{ id: '1', title: `${count}`, release_date: 'today' }]} />}
</ClientOnly>
</>
)
} |
LGTM 🟢 Ready to merge from my side. Let me know in case you object @AurelienLourot @usk94. I made a small change: I think letting the user directly define Also, if you want, a new example We can merge and create new separate PRs or continue on this PR - as you wish. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, nice joint effort from you two :)
@brillout @AurelienLourot Then, may I request to merge this PR? |
Merged 🎉 I can't wait for us to deliver that highly polished component to users ✨ Thanks for the constructive conversation 🦾 (FYI, in case you're curious, I also thought about having both
Sounds good. I'd suggest we create a new page https://vike.dev/react/ClientOnly and document it over there. |
💯 See Looking forward to it. |
I'll look for issues from that label. Thank you!😀 |
resolves #1
I made ClientOnly component according to the https://vike.dev/client-only-components.
I use React.lazy and Suspense to display fallback component until target component is done loading.
Example of usage