-
Notifications
You must be signed in to change notification settings - Fork 657
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
feat: support async route components #1388
feat: support async route components #1388
Conversation
04d6bde
to
72145d4
Compare
Ah wait, marking as draft again because I want to add a couple of tests with |
I think this breaks the |
Can confirm that it breaks the otherwise is really promising I've tested it with Deno.KV executing calls inside the route and it works great |
Thanks for the feedback and giving this a go! I'll look into these issues. |
60a4132
to
5d37d1c
Compare
I think it's ready to be reviewed. I've tested it locally with porting our docs to that setup and islands + twind works there. |
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! Just some nits. Am I right in understanding that this would almost eliminate the need for GET
middleware for rendered pages? If so, wow.
src/server/render.ts
Outdated
if ( | ||
typeof component === "function" && | ||
component.constructor.name === "AsyncFunction" | ||
) { |
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.
if ( | |
typeof component === "function" && | |
component.constructor.name === "AsyncFunction" | |
) { | |
if (component.constructor.name === "AsyncFunction") { |
This should suffice, no?
@iuioiua Thanks for the review! Yes, this PR would eliminate the need for |
Hi @marvinhagemeister, I did also try the PR and it seems like it still has errors with Tailwind (at 2023-07-05, 1 pm in Germany). You can view the result here: https://bjesuiter-fresh-playground.deno.dev/pokelist-async I tried the following: Step 1First I added your branch directly from github to a "Fresh" Playground Repo of mine like this: Step 2Second I added a page with the new async page components feature, which uses the Pokemon API to list some Pokemons: import {HandlerContext} from '$fresh/server.ts';
import {PokeList, Pokemon} from '../components/PokeList.tsx';
/**
*
* @param req Request
* @param ctx HandlerContext
* @returns
*/
export default async function AsyncPagePoc(req: Request, ctx: HandlerContext) {
console.log(`In AsyncPageTest`);
console.log(`\n ---- \n\n Req Object: `, req);
console.log(`\n ---- \n\n Context Object: `, ctx);
// const res = await fetch(`https://pokeapi.co/api/v2/pokemon/ditto`);
const res = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=10&offset=0`);
// const jsObj = await res.json();
const pokeList = (await res.json()).results as Pokemon[];
const pokeListWithImagesAsync = pokeList.map(async poke => {
const res = await fetch(poke.url);
const body = await res.json();
// https://pokeapi.co/api/v2/pokemon/1/
poke.imgUrl = body.sprites.other.home.front_default;
return poke;
});
const pokeListWithImages = await Promise.all(pokeListWithImagesAsync);
return (
<div class="max-w-80 mx-auto">
{/* Tailwind seems not to work here, for some reason */}
{/* Also tried class={tw`text-5xl`} */}
<h1 class="text-5xl text-red-500">Async Page Test</h1>
<PokeList entries={pokeListWithImages}></PokeList>
{/* <pre>{JSON.stringify(jsObj, null, '\t')}</pre> */}
</div>
);
} Step 3I added a normal server-side component, called PokeList to render the list: export type Pokemon = {
name: string;
url: string;
imgUrl?: string;
};
export function PokeList({entries}: {entries: Pokemon[]}) {
return (
<>
{/* Tailwind works here though! */}
<h2 class="text-4xl">PokéList</h2>
<ul class="my-8 w-56">
{entries.map(poke => (
<li class="relative m-4 p-6 shadow-xl text-center border-2 border-gray-400 rounded">
{poke.imgUrl && <img src={poke.imgUrl}></img>}
<a class="absolute top-2 left-0 right-0 capitalize" href={poke.url}>
{poke.name}
</a>
</li>
))}
</ul>
</>
);
} The tailwind problemIf you take a look now to my deployed version of this at https://bjesuiter-fresh-playground.deno.dev/pokelist-async (or run it locally yourself), you'll see that the tailwind classes from the async page component are not applied, but the tailwind classes in the server component will be applied correctly. => The first heading "Async Page Test" should've been "text-5xl text-red-500" (as you see in the page src code above) |
@bjesuiter this was super helpful! Thanks for sharing your test case. Missed that plugins can contribute to rendering too earlier. Got that resolved now and the styles show up 🎉 |
sheet.reset(undefined); | ||
const res = ctx.render(); | ||
const res = await ctx.renderAsync(); |
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.
This doesn't work. Twind relies on thread local state (like context or hooks). Try rendering two pages in parallel
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.
That's a good point, I'll take a look.
@bjesuiter yeah that's a difference between tailwind and twind. They are very similar but not the same. I think newer twind versions aligned more closely with tailwind, but I haven't looked into that yet. I'm hoping we can support both twind and tailwind at some point in the future. |
3269d77
to
4075999
Compare
Wohoo, my next question would've been when this is going to be merged! Does fresh now have some kind of release cycle or is it more like rolling release? |
Planning to do release on a monthly basis. The next one is scheduled for tomorrow if all goes according to plan. Currently writing the blog post, but it's taking me a bit longer than I'd like to admit 😅 |
Very nice! I'm so excited that fresh is picking up some steam! |
This is a quick implementation for #1386 . It allows you to use an async component function where you can load data and return a
vnode
. If the function is async it will receive the request object and middleware context as arguments instead. We fall back to the current way if it's not async.