Giving Up On Next.js | Theo Reacts

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
if you're not already familiar with Brandon he is both the founder of flight control which is a company making it easier to deploy on AWS as well as the creator of Blitz JS which is a JavaScript framework built around nextjs that was focused on doing full stack everything from managing your data to routes to typesafe URLs all the types of things you would expect from like a rail Z framework but built on top of next Instead at one point they actually forked next which they quickly Learned was a mistake and ended up going back to building tools on top of next that said I I'll be frank Blitz is much less popular ever since 3 app started getting attention because we took the benefits of this type of tooling and made it much more modular so yes Blitz was a huge stepping stone but the core Blitz team's Focus has mostly shifted over to flight control now as such Brandon's knowledge and experience with next is very different because it never quite did what he wanted and he built a ton of tooling both in and around next so he he is not on the happy path generally speaking that said he also has a very very deep understanding of next so whatever he has to say is well valuable and I'm sure interesting quick bias check I am a next and versel Fanboy they do sponsor the channel they are not sponsoring this video they have no idea I'm doing this in fact they'd probably tell me not to but I wanted to dig into this because there's a lot of interesting stuff to talk about here so again know that I have biases if you don't like what I'm saying try your best to engage the points not the fact that I'm paid by versel sometimes because this is still really good technology and there are still humans behind all of this so without further Ado let's dive in spoiler the conclusion was we'd go back and choose remix if we could so this going to be a spicy one boys nextjs app rer migration the good bad and ugly last year we rebuilt the flight control dashboard from scratch with the next app router the old dashboard was built with the pages router the old dashboard did the job but the UI was designed by me an engineer and felt too much like a prototype it was time to grow up and bring in some real design Talent we partnered with overnight to redesign the entire UI they designed an incredible work of art while we loved it it required some major changes because it wasn't possible to build this with the next Pages router we needed nesting routing and Shar layouts I don't know if I necessarily agree it's not possible it's definitely not pleasant if you're not familiar with the concept of layouts think of it as nested UI wrappers based on which route you're on so if you're in the dashboard page it would have your topnav and maybe the like name of your app or whatever that you're viewing but then if you go to dashboard users it will still have that top nav but it might add something to the left nav that idea of specific elements being added into the Container as you go deeper into the URL is often referred to as layouts one of the cool things that was introduced in app router is the idea that when you have a layout. TSX file for dashboard and then you have something inside of dashboard that layout still gets put in the page so if you had the root had a layout that had your background color and then you had dashboard which had a layout that had your top nav and then you had users which had a layout that had the sidebar now it's going to render the top container with the background color an internal layer with that top nav and then another layer with that sidebar and just renders as children until you get to the actual page element it's a really nice pattern that didn't exist before but there were ways to do it I won't say they were were pleasant but they existed I know that because we did a lot of these at ping the way you would do layouts previously is you would make your page function the way you always would with the old Page's router but you would also assign do get layout to a function that took in the page and returned whatever content you wanted to return here so on every single page you'd have to put all of the different layout layers that would wrap the content so you would be rendering these pieces every time you navigated and it it wasn't great I'm not going to sit here and pretend this was a good pattern but it did work somebody in chat actually a very relevant person pointed out in chat Derek who if you're not familiar did a lot of work getting next at twitch for the mobile site real real real early pointed out that the pages method was terrible if you had to do any sort of data fetching which absolutely was true and it wasn't real nested routing yes it it was not truly nested you could fake it well enough and I know that because we faked this a lot with paying but you could do it anyways they pointed out that they needed the nested routing and shared layouts you could kind of do shared layouts nested routing not great but somewhat fak regardless I understand why they would want to switch a full rewrite was required so we considered all the react options at the time which was in April of last year the next app router remix and tanack router for True single page apps app router and react server components seem like the future because they're part of react and NEX is the most popular framework although it was still bleeding edge it seemed like the safest long-term option I want to dig into this April 2023 bit we released upload thing on May 1st so this was around the same time and it was early I'm not going to pretend it wasn't like you can tell how early it was by the size of my mustache this was quite a while ago it had rough edges and I will be honest we would probably not have succeeded with our early adoption of app router if it wasn't for the fact that I had one of the lead contributors JJ on speed dial fixing our bugs as we ran into them we ended up being not just one of the first production app router apps one of the first production app router apps running on edge and I was real real proud of that so the same shirt no this one has a checkered pattern this one's floral nice try though so if you didn't have that type of deep connection with versell and the ability to get them to prioritize your bugs adopting this early sucked but the way I used to put it was we bled so you wouldn't have to I chose to eat this cost real early knowing it would be broken in a lot of ways knowing that creat T3 app still wasn't using App router because it wasn't fully stable at the time but we chose that because we were so excited about the new patterns that we were willing to be the ones to run into the wall so we could leave the hole for everyone else going forward I don't know if Brandon wanted to be that I bet he thought he wanted to be that but I don't know if for where he was with his business and what his long-term goals were if that was actually what made sense for him so going forward know that we started at around the same time but I think our mindset going in was very different where I wasn't trying to rewrite a stable thing I was trying to write a functioning thing at all when you're doing a massive rewrite moving to an unstable thing is terrifying so this is an interesting setup let's see where it goes the traditional Spa approach was attractive because rsc's are complex and blend server and client having a pure client in clear separation from the back end has obvious benefits we We Care tremendously about type safety So Tan stack rotor was the only option it seemed very promising but it was still Alpha at the time and unclear if or when it would reach production I will say like honestly if I was rebuilding this from scratch and doing that heavy of a dashboard tanac would be on the top of my list for options unfortunately we barely gave remix any consideration partly because we already used Blitz off and RPC so moving to remix would also require changing these two parts of the stack that could otherwise remain with next with app router chosen we got to work the migration to the next app router following points are directly about our experiences using this for web app dashboards undoubtedly there are more good and bad things for other use cases agreed good was layouts we needed nested layouts for our right side panel UI and navigation like environments environment ID environments environment ID deployments deploy ID we use this for upload thing as well I I'll show the upload thing code why not so here is my upload thing dashboard it has this top nav and when I go to one of my projects like image thing test you'll see the top nav changes to include this additional piece and we have the side nav here and when I go to the different options here the snav stays it it's different states but it's still there so we take a look at the code for this I have Source app dashboard I have these layout helpers that I escaped but what we care about is initially the homepage so you have the page this has all the different content that's on this page but more importantly we have the layout I have this helper Dash layout this is where we handle things like which groups you're a member of which options you can select from our theme provider and a bunch of other pieces that are necessary for the dashboard we also have in here yeah breadcrumb component so this component is the top nav component we have an app ID selected it shows the app and if we don't it just doesn't no prop app ID then we show or if we have the prop app ID we show the app switcher with that app ID otherwise we don't so now we have this as that top layout layer and now when you navigate deeper like if we navigate to a specific app ID we again have a page it doesn't have the side or anything this is just the page contents for that root page we care about is the layout again so we're still wrapping with the dash layout we're doing that because home and app ID actually have different layouts and I architected it like this so that we could pass different props and behaviors to the root layout versus the app ID layout they're still using the same component so it's not nested in that sense but it is nested in the sense that they're sharing parts and they share a top level parent as well we can also go one level deeper if we look at something like the files page and see that this layout has the dashboard shell helper for our root like section for that page but it doesn't include the sidebar or the topn because those are all included by the layout above the way to think about this is if you're in a given page the layout for that page you can just go up one layer at a time and see all the different layouts that are going to apply just by looking here's a layout we go up further here's a layout so this gets applied then this has a first child of this layout which as a first child of if we had a layout here that but we don't so it's just the page really nice pattern and I see why for a dashboard like what we did here it's very very compelling so let's take a look at how they built it this is impossible to build with Pages router but doable with router okay again it was possible with Pages router it just sucked each layout persists so navigation between sibling pages does not unmount and remount the parent layout again really nice you're only changing the components inside of the layout when you change Pages rather than changing the whole page when you do it you can't Nest pages so it was awkward to build this UI the environment UI has to be inside the environment layout. TSX with the environment page. TSX only containing return null interesting I don't quite follow what it means here you can't Nest Pages oh so this has a different page nested here this would have been useful to use the weird route hijacking stuff I don't love it but it it could have worked here my regard I see what they're saying here so like if you had a page that wasn't accessible like environments environment ID if you didn't have any content on that page you always went straight to deployment but you wanted this layout to apply for other things here you'd have to have a null page there for that to work which is a weird workaround I understand the pain there we've talked enough about the layouts now we have to talk about loading States I love the new loading stuff in suspense so I'm happy it made this into the good because it's really good stuff when navigating to a new page with react suspense you can show a loading spinner to the old UI or the new UI depending on the desired user experience this is a react feature and is now usable in next because of the app router support the traditional spinner on the new route is easily achieved achieved with a suspense boundary around it this is again really cool have a bunch of videos where I talk about the suspense stuff you can just have a component that does a sync a weight in it and if it's wrapped in suspense and it can run on the server then you'll just have the loading spinner until the content comes down all as part of that first request really really good stuff like groundbreaking potential performance check out the Jack Harrington video If you haven't it showcases this stuff really well the new possibility is using react at used transition on links to show a spinner in the old UI once the new UI is loaded the UI switches instantly the benefit is that the user can continue looking at useful info while the new page loads in the background this does have catches too like I'm on the uplifting dashboard now I'm clicking this now and I don't have any indication that the page is loading at that point we probably should we should almost certainly put a custom used transition Handler in that view so that it shows something else while we're waiting for the next page you don't have to and it navigates the same way like GitHub does but could be a nice change things to think about the cool thing about the new model is you have that control you can choose when you do or don't show these states now which is dope the result thing us is nice with the developer experience is Clumsy yeah if is pending start transition use transition routers use router onclick start transition about yeah there should be better page transition handlers I know this comes up later in the article but the idea of having custom transitions when you route from page to page is something that is important and doesn't really exist in this new model yet I see Ryan carniato in chat creator of solid if you're not familiar pointing out that people always report this as a bug but it's more an advertisement of pending patterns yeah cuz you have control of these things now which you didn't before again keep in mind you still need the suspense around the page for showing the spinner on that page if the user navigates directly to it like a browser bookmark or what I do which is just type things in until you end up where you want to be still part of the good the developer experience of initial data loading on the server this has never been Tau I genuinely love how the new next model handles these things it's to be fair just server components but it's so nice being able to load data directly from the server like this project data is from their at domain project so this is their internal thing for fetching data about projects and now the page just gets the data by awaiting that call also notice they forgot to make this an async function and this simp it and also they're not code syntax highlighting I don't dig too deep into those things but this should be an async function regardless nice example you got the project data you return the project page and as they say here it's proved useful mainly for the DX of initial data loading using the following pattern yep yada yada and then in the client component they used tack query for live data updates with polling the initial data is passed to the Ed query hook via the initial data option I expected to get better initial load performance since this was so highly marketed but in reality I can't tell a difference between this and client side data loading at the end of the day this DX might be slightly worse than just having use Query due the initial data fetch on the client because have to explicitly handle initial data this depends on so many different things an important piece to note here is that project data is all of the data for this page they have a single function in this example that gets all of the data that this page needs and I agree in that case the only time you're shortening is the time between the first request so if we open up my favorite app user requests page and on the other side we have full page content loaded so in the traditional single page app model first thing that happens is the user makes the request user loads HTML user loads JS tag because there's some JavaScript tag in this HTML that's where the single page app part comes from so you get that JavaScript that then generates the page content JS renders components and then JS fetches data from server and now you're seeing I have to make this longer this why I normally do it vertical because I need the extra space so once the data fetch request has happened this would be like imagine you have react query inside a component that just got rent Ed by that JavaScript and now it needs to go fetch that data so now the javascript's fetching that data server returns data and then client reeners with data and this would be the end this chart is a little misleading almost intentionally so because in reality there's a couple parts here that more often than not are going to have huge gaps can you guess where they are looking at this can you see the areas where code runs the longest if you ask the quick guys they're going to focus on this part and they're going to focus way too much on this part the harsh reality is the problem tends to be felt most here and here these are the places that tend to be the slowest especially if you're a user in a different region because once you've made the request if this is cached HTML that exists on a CDN this might not be that slow but if it isn't if this is generated through something like I don't know a remix server that's running in a single location this will take quite a while so much so that the distance between these things is misleading this part probably going to be the part that takes the longest because the things your server does to generate the data the client needs probably requires hitting a database authenticating the user for writing the data processing the request doing a lot of other things and if it's a serverless it might even include a cold start so this part tends to be where things are the slowest especially if there's a cold start which again if you're building on top of serverless technologies that cold start's going to cause this to be significantly longer sometimes even seconds before we go into the new model I'm going to change the color of this quick so that we can represent these both more easily so the request has been made if we're using partial pre-rendering which I have a whole video about if you haven't already seen this should take roughly the same amount of time I'll say PPR shell return so I don't feel like explaining PPR check out my video on it if you haven't already seen it the tldr is that every route can and probably should have some static HTML that represents the top of your route most single page apps just have a blank is HTML page for every route on the site and then the JavaScript does everything depending on what your url is with the new model you have a different HTML page for every different page and those pages can differ more or less depending on what you specifically choose I think that's awesome let's dive in a bit further because while this PPR shell is being returned we're kicking off that background rendering at the same time all the data the page needs has started at the same time as that initial request so this might take some amount of time but while that's happening and this shell was returned we are loading JS fonts Etc which by the way I didn't include in here when the JS tag loads it's also loading the CSS the fonts and all those other things and that has to all happen before we can render the components that then figure out which data needs to fetch that then triggers this data Fetch and then eventually whenever this is done we'll say here I'll say data streamed n at this point your data can stream in whenever your server finishes whatever it has to do to authenticate the user fetch data from your database cold start whatever it does you're still loading your JS and other things in the background or even visibly on the page with good loading States while this is happening the magic thing I'm trying to highlight here is that there is a gap in the old model from when you load the HTML and when the server starts to figure out what to send you after so again if you have server data that takes some amount of time to load and this part also takes some amount of time having to wait for all of this to happen before you can even start the request for the right data sucks you're guaranteed at least this much time or however long it takes for your browser to load these things from its cache however there are things that make this feel less bad one example would be these things all being cached that kills this initial request so you have that HTML and as long as the JS tag that you have is still valid you can start from there and then once that loads into your browser you can then make the request that said if that JavaScript bundle has changed you know have to wait for that still not the best experience but better you can also not use things that have cold starts you can do things to make this time less bad I know for example that the flight control guys don't believe in serverless they build everything on top of fargate if I recall because they think cold starts are evil and they want to make it so you can avoid them entirely I think cold starts matter a lot less when you have routing patterns like this when your page can be loading other things while the server is cold starting and responding to your request but that's the big difference I want to highlight here is that they've already made this small enough that once this is all cached it doesn't really feel particularly meaningful whereas when you can do those in parallel now that's a huge benefit to a ton of users a lot of the time it also means things like serverless Edge Technologies and stuff like that are a lot easier with this new model too I will say though it is very different and even though in this example I made the loading time here much longer you'll see that intuitively the whole time is shorter as soon as you parallelize these things I love this remix is doing some similar stuff in particular sending headers down to start getting your CSS loaded as quickly as possible but this level of static CDN level returns for partial assets while you're kickstarting the actual server response is something that's unique to this new model so again this makes sense if their fetching times from the server are already pretty fast and they have all of the JavaScript and stuff cached on their device but if a user is going to that page and they don't have that data cache because they haven't been on the page for a while now they don't even get to start the server side data fetch until all of that stuff has happened which is not a great Point there's a huge value in the server getting to own that initial request if you benefit from this parallelization doesn't seem like they benefit that much from it so so that's again why they didn't see much better initial load performance if they didn't have any caching they'd see a bit but if you're close to the server it's not going to be a big deal and I agree the DX of having to pass an initial data thing in and then update with used query this sucks I absolutely agree in fact I would have put this in the bad because this this is really bad speaking of bad we're now in the bad section bad having to add client side fetching for live UI updates seems like server component should be able to support the same SWR semantics as tanet query and also polling but it doesn't with next you have to add client side data fetching for this and we want this for almost everything in our UI this results in a lot of duplication as mentioned in the previous section about serers side data loading bad serers side errors easily swallowed or hidden if something errors on the server and you haven't added an error boundary in the proper spot it will render the suspense fallback instead and try to render the page on the client this results in errors being thrown and logged but the UI appearing to work fine overall it's very confusing and hard to trace I haven't run into this one just yet interesting I wonder if this has gotten better in newer versions because he mentions later on that they're on an older version of next it's like still on v13 but as far as I know we haven't run into this Mark if you're here correct me if I'm wrong but I don't think we've had this issue another bad is that they can't Implement route exit animations this I absolutely agree sucks the next router needs some better way to handle animations and it doesn't have it and as a result using something like frame or motion works great when you're opening a page but it just doesn't work for exits oh man the lack of routing type safety I complain about this a lot actually somebody in chat right now what plan Ethan ncer who youall might know from his YouTube channel who made a library to add typ safe routing to next it seems like they've also built a copy pastable solution for flight control too obligatory next type safe url. yep as you see here there's already a library that does this or you can copy paste the code that was provided by flight control lots of good options and now we're into the ugly the first part is the abysmal Dev server performance it's a lot better now than it was 9 months ago but it's still unacceptably slow as one of our Engineers put it the dev server performance is so bad I would give up all the good features and a heartbeat to avoid it I would even switch to an alternative framework just to avoid the next Dev server I'd even switch to a different language that's how much I hate using next zap router it was bad it was real bad initially and I have a lot of thoughts on this I could probably do a full video about the dev server performance stuff cuz it was bad that said the big issue with this is that versell made a bet and I think that BET's taken too long to pay off that bet is Turbo specifically turbo pack turbo pack was meant to be the rust replacement for webpack that you could drop in and just stop using webpack and start using this instead and it would solve all the bundle Time Performance issues that existed in next and then it took way longer than they expected like hilariously longer than they expected I think that's the issue they didn't prioritize performance because they were waiting for Turbo pack to come and save them and then it just didn't it's a lot closer now in fact I actually have turbo pack running on projects for the first time as of the most recent release and it is hilariously faster it is a significantly better experience here's the code base for a tutorial that I'm working on and you'll see here my next Dev command uses Turbo still took two seconds to start but now go to the Local Host page oh is my database down right now yeah it is we'll ignore that so I'm going to add this hello div yeah page compiled in a second and if I change this to something else I will open that up here save will it load the page until I open it it won't it would be nice if the page being the background didn't block that that hard oh does it not even rebuild that way let's just pull up the browser instead cool I am pressing command s right now that's instant that's instant changing it to something else I am command sing now that's great that's instantaneous someone pointed out that I'm using the canary that's cuz I was testing next 14.1 real early but this is just next 14.1 if I change this to 14.1 point do they do a DOT update no they've not cool pnpm install cool we are no longer on an early access version so once again I'll go into page DSX close that open this up pnpm Dev refresh I'm going to change this to hello world and I am saving right now it's instant commanding Saving right now again instant like it's fine this this is as good as my experience with v they it's here now like this was not this good before I'll show without turbo which to be very fair I am on an M2 Max Macbook so it's a very powerful machine we can still see the difference refresh this it's compiling still took 2.2 seconds and we're getting some webpack bitching at us of course reload the page change this to hello Saving right now still pretty fast but Saving right now it takes time though it's not like it feels like next frame versus a delay you can see here how long those changes took it's like 270 milliseconds 140 milliseconds if you want to test these things more in- depth they actually have some nutty demos here yeah they're with turbo pack example a ton of stuff in it so you can actually see a significant performance difference if you think I should do a full video about turbo pack performance let me know and I will do that in the future because it's finally at the point where I'm excited about it but again is just one of the many things that took too long it is now there which you might be noticing a theme here a lot of the issues they're running into are early adopter problems speaking of which the dev server memory leak this was a problem for us really early but I feel like we haven't seen this for a while my typescript server crashes a lot but I don't think our next server crashes too much again I'll have to ask my CTO Mark or Julius if he's here to let me know if we've had this more but uh I don't think we have this too bad we definitely have like weird cash states where a page doesn't look right and we kill and reset the dev server to fix that but I've never had these memory leaks yeah no Dev server crashes according to Mark who spends literally 10ish hours a day almost every day working on this and he leaves his next Dev server up for days at a time and doesn't have that issue I'm not saying it's not a real issue I'm saying that it's a limited issue and it's also possible it's an incompatibility with some other technology they're using or a weird implementation detail of some of their like Blitz RPC stuff or their type safe routing stuff there's a lot of places this can come from that aren't necessarily nex's fault it sucks that they're hard to debug and that NEX probably isn't helping but it's not as big of a deal at least we haven't experienced it then the hard to trace errors yeah this I will say is a problem many errors you encounter are super vague with no traceable call stack leaving you to trial and error like binary search by deleting half your app at a time I agree the errors aren't great especially in the older version they've gotten a good bit better since the original release of app router but they're not there yet I will counter this with a huge Improvement in my experience running into errors overall which is that I found errors and unintended behavior in react especially post hooks didn't happen when you were developing it happened random times the most common example is overused use effects causing a ton of refetch that shouldn't be happening I've seen people dos themselves and cost themselves hundreds of thousands of dollars because of a bad use effect in the wrong place this model trades those unexpected long-term consequences for annoying short-term feedback when you do something that the new model doesn't like it immediately throws errors at you the result of this is you see more errors and also those errors aren't always great but that's actually an improvement believe it or not simply because you run into those errors while developing as such you can often think back to the things you've recently changed and identify which one I know that he mentioned the binary search by by deleting half your app at a time but you only have to you only have to delete from the changes you've recently made almost always I've had one or two cases that were different and almost all of them are datetime rendering on the server versus the client but I've had a great experience with errors in the new model not because the error messages are good but because the errors happen so early in the dev process that I can catch them much more aggressively so hard to trace in the sense that if you're coming in out of nowhere with this error and you have to figure it out that sucks but when you're developing you run into these much better overall also ugly it was marketed as production ready way too early it took almost a year after it was called production ready for it to really be usable in production there were so so many bugs and issues early on it was absolutely miserable thankfully many of those are now fixed but the bitter taste remains first point is that app rouder didn't go stable until May I don't know about y'all but may was not almost a year ago took almost a year after it was called production ready for it to be really usable in production it hasn't even been a year since so I'm confused about that point more importantly though production ready and stable both mean very different things to different people for me production ready means that it runs well in production and doesn't have unexpected consequences once we have validated behaviors stable often doesn't mean anything about how well does the service run it means are the apis stable are the actual things that we are calling going to be supported long term it doesn't mean how well are they going to run it doesn't mean how good are they for debugging stable means these things aren't going to change and I hate that we keep using production ready to describe the things we don't like instead of the things we do and I agree we shouldn't be using these terms for things much at all I don't know when and where verell called app router production ready or what they intended when they said that but I would say for my use cases and for a lot of the businesses I work with that vary wildly in size and scale app routers absolutely production ready and the apis the things that we're actually building into and on top of those are stable they're not changing they might add new things on top but the core pieces that Define the app router are stable so this is a semantic definitions thing but you swap production ready out here for like battle tested good debugging and ready for teams of any size it wasn't there yet for sure and I can see why production ready is interpreted that way or even interpreted to mean everything is 100% perfect ready to go but honestly the section only matters depending on what your definition of production ready is that said I don't think it's up to versel to determine what is or isn't production ready the best they can do is call things stable which is what they have done as far as I know from vel's marketing okay they say here with the release of app router 13.4 you can now start adopting app router for production I can see why people would read into that specific ways Fair stability means the core of app router is ready for production has been validated by both our internal testing as well as many next early adopters there still additional optimizations we'd like to make in the future including server actions reaching full stability it's important for us to push towards core stability help provide Clarity for the community on where they should begin learning and building applications today the goal here was to let people know what the recommended path was calling it stable wasn't to say everything works perfectly and is a really great experience at every level for every person it was a comment on how vers sell is thinking about these things now and again they still think you can use Pages router they plan to support it for a very long time and we still use it in production for a bunch of things too you don't have to shy away from Pages cuz app router is cool now now we'll go into the next ugly point of over complicated and opaque all of the above leads us to the conclusion that next is overly complex and complicated when something goes wrong there's no way to work out why or how to fix it we have certainly wasted a lot of company money wrestling with it again I like that with the new model things go wrong much earlier and also with the new model there are a ton of ways to opt out of things there's a lot of nuance is being missed here which we'll dig into once we finish so don't leave when the article's over cuz I really want to dive into some stuff caveat we're still on next 13.5.3 we've tried next 14 and things were breaking so we haven't been able to prioritize this upgrade yet seems there was a major bundling change of 14 and many folks have had challenges upgrading I haven't seen this much yet if anybody has a link in chat for people struggling to upgrade to 14 I'm curious here's where they make the point of we'd go back and choose remix if we could aside from the much better Dev performance I think remix has a better architecture and abstraction for example with remix the user owns the client and server and entry points but next owns everything preventing you from doing anything they don't explicitly allow unless you use npm patches which we've had to okay I think I found your problem I wish that it started with this I I have frustrations now because having this and also having this and not indicating that said memory leaks might be because they are hacking in their own changes to next feels a little bad faith to me I was doing my best up till this point cuz there are a lot of valuable parts of this article but this you shouldn't be consistently patching next I could be misinterpreting this and they're patching other packages that they're installing so they work better with next but if they're actually patching next that's terrifying but again like Brandon is already forked next so I wouldn't put that past him I want to showcase how to work around some of these things I already did the tangent about turbo and how it fixes a lot of the dev server build Time stuff but I really want to go into this because even though we put it under good this part I consider really bad this is the thing that I agree the most on and I hate the most so how would we fix this in this project which we just scaff with create T3 app we have a server folder has our drizzle schema if you're not familiar with Drizzle it's an RM for typ script that I really like check it out if you haven't already we have the schema which here we have posts I want to update the posts and the easiest way to do that quickly is to use the drizzle studio so Bun Run DB Studio it's a command that I think we already have here cool yeah drizzle Studio really nice now we have the table might notice that prefix that's cuz one of the cool things offered in drizzle is the ability to put a prefix in front of everything when you create values in your table so you can have one database being used for many projects really nice if you're using something like Planet scale with the free tier that only has one project so we have the posts table has an ID name all this other data let's just go make some fake posts quickly here little ID default name first second one more so now we have three posts here let's go use these posts so with traditional server components assuming that they never update we could get these by changing this to be an async function const posts equals await db. query. posts. find many and now we have the posts we going to delete all the content here and instead we'll do posts. map P return div p. name and since this is react needs a key key equals p. ID so now we have this we can go take a quick look at the page wait you unsubscribed you know it's free right just hit that right button at the bottom of the video come on making all this content and you're not going to hit the Subscribe button helps us out a lot but you'll notice the page content didn't change when I did that I'll actually keep this open in the background so we can swap back and forth I'll change this again and go to the page it hasn't changed if I refresh obviously it'll change but what if I want that to change without having to refresh what if I want that to change with things like what react query does like when you refocus a window and it refetch things in the background well we need a way to get this data into react query the thing that was being discussed previously in the blog post was that this data would be passed down to a component that would then do the data fetch so if I made a posts. TSX and it was use client export const posts view equals props we'll say I was going to any this for now because I don't care return div now I should have the posts model let's do that right actually posts is I need to infer the type off of the schema yes iner select model type of posts I'm assuming so an important piece here is that these are just type Imports and you can't import posts on the client because this is part of your database you can't do database Imports or database calls so if I just took this here it wouldn't work because you can't call the database inside of a client component so how do I get that data there the first thing we can do is pass it so I'll put posts view here posts equals posts we have the post prop post view we can pass the posts they're passed here I'm going to paste the code that we had here before cool typing is hard okay anyways now you're have the post view we're passing the data over we look here it's no different but again if I change this once more nothing's happening how do we get this data updated over here I'll even add a button we'll have this be button update class name equals P4 BG red 800 cool so I have this big red update button at the bottom but when we click it nothing happens we haven't found an on click what would we put here to update that data the first option is to use the new router stuff so we could use const router equals use router from next navigation and you do router. refresh so now I'll go here I'll change this fourth times the charm saved nothing's changed I click update now it updates because it's refreshing all of the data coming in through the server components this is not ideal because it's not automatic it's going to change the Dom structure and could possibly cause like an input your typing in to break and change and there's a lot of reasons you don't want to just constantly refresh all of the data coming in from the server what if we could use the old react query way what if there was a balance here where yes we would lose the initial data fetching pattern but we could get back a l lot of these same benefits I'm going to make a new folder in server called Data layer and here we'll expose posts. TS I'm going to yink this guy export acing function get posts and this can just return the awaited results of the DB query I will import that obviously I can go call this function over here and nothing's going to change this does exactly what we'd expect it to patial loads everything behaves how we want but we would want to call this on the client uh I will wrap at this level query client provider so the problem here is that this isn't as a client component so let's add one more providers. TSX so we're doing here since the layout is a server component and honestly the layout should probably always be a server component I want the query client provider to be a client component because that's how it works I can wrap with my provider's component theoretically that all works now so again the reason this is important is client components can be past server components as children so you can have a server component route for HTML you have a client component right below that basically as your route but since the next router handles these layers for you this just gets past children in this case the children are server components they could also be client components lets you do routing through things really really powerful so we can still have that root level set of providers that are a bunch of context providers like you would need for something like react query so what are we going to do with this this is where things get interesting we're going to hop into the posts. TSX view because we want to use this data in here right now we have the router refresh solution let's destroy some stuff so I'm going to just I guess this the part that we want to kill so we're not going to use the router for this anymore we're going to use react query we're going to use it to get the data too so const data is loading equals use Query so in here we need a query key which we say posts we also need a query function this function needs to get this data could be sync or async doesn't really matter does query does has to be a array now yes it does cool so we need a query function but again I can't just call the existing function here because this is a server site function so what do we do about that we have this database function we want to call this on the client even though it's a server function if you put use server at the top of the file this tells the react compiler hey by the way all the exported functions in this should be accessible via post so now if we call them in the client we can use that data so if I call this here instead now with get post is the query function react query we just get the data one quick gotcha is that query function passes a bunch of additional data to whatever it calls and we don't want that cuz that might not be useful over post requests so we're just going to shim out with a quick in line function the important piece here is that guest posts is an asynchronous function that makes a post request not posts like the thing that we're making here but a post request like the HTTP thing just like you would with a fetch an alternative way of writing this would be fetch whatever API posts do then Json etc etc and then you go make this route that returns that Json what's happening here that is really interesting is part of server actions is you have the ability to call them the way you would call any fetch call so by putting this get post call here and exporting this with a US server call on top we have now made this effectively an RPC that can be called from any client component so now I have the exact same thing I would have had if I just used react query in a bunch of fetch calls and I also have the refetch helper so if I grab this refetch Helper and call this here instead we'll do void because this is async and it knows that so you can wait and do things here now if I go back to the page we'll see first second fourth times charm hop back and drizzle fifth save it go back and wait it already changed this is where react query gets interesting if you already know this Behavior about react query it's actually super super handy when you change window focus it will by default revalidate anything you fetched with react query this is super handy if you have like an old page and you reopen it you'll get fresh data as soon as you open it there's a really good page about this in the react query docs called important defaults I used to change a lot of these but I've since learned to love them one of those changes is that when the windows refocused it refreshes some of those defaults include things like the window refocusing causing refresing or or the network reconnecting or a refetch interval all of these things make it so your data is less likely to be stale there's even a default stale timer if you want it to be longer or non-existent you can set all those things but all the defaults here are actually pretty dang good and if you want to use all of those with the new model here's me using all of those with the new model but what if I want that loading state to not be there I want to fetch that on the server well thankfully we're already doing that so if I do props posts props initial data props do posts since these are both literally calling the same function the types St perfectly synced it's actually one of the really really cool benefits of the new model is that I can write one function this get post function I can call this in the page is it to pass the post so I'll add those back here and now this one get post function here is it getting called on the server and here is it getting called on the client with good defaults so now if I load it it's always there the annoying part here and I will cave this is still annoying is that you have to pass this server fetched data down as a prop and then deal with it that way there are hacks for this someone could make an updatable server payload help where you would wrap this function with something you would pass it down and then you unfurl it with a hook and it does all of these things for you automatically I could even probably do a demo of that so if you want me to work on something like this in the future let me know and I'll put together an example the point I'm trying to make here is that all of these things are optional and in your control the level of flexibility here is honestly surreal if you don't want this behavior and you want things to work the way loading always worked you just delete it now this is basically the same thing we always used to do with react query and something like trpc we don't even need TR anymore we still get the type safety because we know the type of this function if we need more things like we want to be able to create a new post let's do export async function create post and in here we'll return await db. insert I think I need to import posts here do values and this needs to be a new value we'll give it a name of added by client so by default if I call this instead of the saying update we'll say add new I'm going to kill the refetch call and just do this create post call so now we have have this await create post call when I click the button huh nothing's happening that's cuz nothing's told the page to update again this one of the cool things with the new model is I could have specified in here what to change but instead of doing that we'll just put this at the bottom because now we have to wait for the post to be created and then we'll refetch after and now everything works how we expect really really nice we can even use the use mutation helper so const mutate equals use mutation again react query op as hell we need a what are the functions even called with this new model mutation function again we'll do create post so now we can add this here and we can even add classic react query Style on success refetch and now all the way up here we have all these different behaviors defined we can even do one of my favorite things which is a custom hook for this data so I'll do function use posts data now we have all this and we can return data mutate and now we have the super clean abstraction const data mutate equals use post data kill the loading state and all we have to call here is that mutate don't need to AIT it CU it's handled for us now and now we can add new things to the client that easily doesn't that look like the good old react and react query that you're used to the difference is all the functionality that we've just defin is done via server actions so we get all the type safety benefits all the potential to use the router ref fetching stuff in the future all the buy into the new stuff without having to leave behind the way we're used to doing things again we're not pre-loading the data here but you can if you want I agree the DX around that isn't great but it's not bad and the result here is way better than my experience was before previously I would have had to stub all of these things out via trpc and call them which was again way better than the previous option which was blindly writing a bunch of things in an API route fetching them via Json requests parsing them and hoping they're the right shape now you just import the function and you can literally just import the function like I am importing create posts and get posts from that server file this is dope the point I'm trying to make here is this part that having to add client side data fetching for live UI updates it's just as hard as it was before it's no harder unless you want to use that initial data I would argue that previously things were as bad if not worse than they are now yes if you want to take advantage of that initial data loading you have to pass values around which sure not the most convenient thing in the world but the benefit here of being able to write these functions that work on server components and on client components you just import them and call them and use them with whatever you like like in this case react query this is react query working seamlessly with new server component stuff if you haven't used server actions like this before I can see why you would feel like you have to take advantage of the new model in a different way but here you go this stuff's gotten really good and I think we should encourage these types of patterns more because I think people are really scared of reaching for used query reaching for used client these aren't bad words you should fear these are different ways you can build and if these are the patterns that make sense for the thing you're building if you have a bunch of stuff that's changing regularly and you want to be able to update your data more trivially there you go it's not that hard think I've said everything I have to say about this one I do really love that we're getting feedback from people like Brandon about the current state of app router I don't love this blog post but it is important that we get to think about these things and make decisions as a community about how we move forward I'm very happy we chose to bet on app router with upload thing and a lot of the stuff we building at ping but if you bet on it early there were real pain points and it sucks to see people experiencing those I don't think any of the points made in this article are a reason to not use app router though in fact most of them have since been resolved or as I showed there they're are better ways to solve these problems than previously existed in the old Spa model I'm curious what you think though should we be starting with remix instead or should you still try out app router are you now more or less hesitant to try these things out let me know in the comments I'm genuinely really curious because I hyped on this model and I understand not everyone is I'm just trying to understand why that's all I have to say see you guys in the next one peace nerds
Info
Channel: Theo - t3․gg
Views: 88,223
Rating: undefined out of 5
Keywords: web development, full stack, typescript, javascript, react, programming, programmer, theo, t3 stack, t3, t3.gg, t3dotgg
Id: 9jqPbNgzHOA
Channel Id: undefined
Length: 44min 48sec (2688 seconds)
Published: Wed Feb 21 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.