We also have this idea of a query function. So this is how a loader looks like in Remix, the initial code where we authenticate the user, create API clients, instances, and things like that are expected to not fail. So they fail, we want the error boundary to render. We then try to catch everything that is expected that may fail. If there is an error, we capture the exception and send it to Sentry. And we also return or true a response to let the user know something went wrong. We true when the word root is bad, and we return when we can still render part of it.
And the query functions are these get something functions, async functions we have inside the loader at the end of the loader code where we fetch data from the API. We also filter it or parse or transform it there so the word code is in a single function. And inside this function, we can try catch the request. So this means if you get donation fails, in this case, we want the root to fail because if you are seeing a specific donation, nothing makes sense without that data. But if you get charities fail, we can return null and catch the exception to center. So we know something failed, but for the user what's going to happen is the list of charities disappears from the UI. So important queries cause the main catch to fail inside the loader but the others fail silently.
We do something similar with mutation. So mutation is again a function we put at the bottom of the action where we created the SOD schema, we validate the form data with the SOD schema and send it to the API. And if it's a success, we can return it JSON with the status success or a red or whatever we need. And if there is a random error, we check if it's a SOD error, we return in JSON with a status error and the list of error messages. If it's not a SOD error, it means something else failed, we got your reception and show a message usually telling the user to contact support that we already know of the error.
We also handle errors on the UI this way, so we have the cache bound that in case the wall loader failed, the wall root failed. But we move a part of the UI from the root to different components, and inside those components that live in the same file as the root, we call user.data, get the data we need, and render it, and if the data is null, we just return null to hide that part of the interface. We also define custom conventions using the handle export. Most of this custom conventions are no part of Remick Utils, things like Hydrate scripts, and Dynamic Links and Structured Data are part of Remick Utils, and we have this global type, Daffy.handle, that we can attach to our handle export and define what the root can set there. So our roots define if they need or not javascript, if they need external scripts, it can load it that way. We also extend this convention for different layouts, so the boarding handle have different conventions attached to the global ones. Same with layout on the app handle and you can see for example in the app handle we have this aside component that receives the loader data props and this allows the app layout root to render the sidebar with a side, but render it in a larger layout with the root specific children routes inside it in another place. So we can use this as a way to define slots or then selling it in some way for different parts of the layout. We also have a type safe API client so we don't use something like Prisma because we fetch an API. So in order to have type safety when we fetch from our own API we define a schema with sort, export, infer the type from there, and when we fetch the API we just parse the data as it comes to from sort with sort, and let it fail if there is an error. So if there is an error there we know something changed on the API that wasn't expected to change and we can review we review the error and check what happened and if not and if something worked correctly the return value from the API method is going to be correctly typed with the schema donation type.
Comments