Re-enable Photo Upload0:00
In this lesson we're going to update the new puppy form so that we can persist our puppy creation to the database. Alrighty, let's scroll down to our new puppies form. So if you remember up to this point we had the profilePic field disabled because we wanted to wait for a backend to process the image. And so maybe the first thing we could do is go and re-enable that field. So I'm looking for the profilePic field here.
and re-enable that uh, field. So I'm looking for the profilePic field here and you can see it's disabled. So let's remove that. And also I had some styles like cursor: not-allowed and opacity: 50 that I should have scoped to the disabled states, but oh well. And so I've removed these and so now we should be able to choose a photo. So I've organized myself with a few pictures just for
and so now we should be able to choose a photo. So I've organized myself with a few pictures just for that lesson and uh, these are mostly my dogs. So cute. All right, so we'll select this one. And if I try to submit, remember we have client side validation. So Flex and T-Rex Loving Bunnings. Bunnings Warehouse is the Australian equivalent of Home Depot. This is where the photo was taken.
equivalent of Home Depot. This is where the photo was taken. So I have all the fields filled, but our form is sort of bogus to this point. So if I submit it, it's going to say it's adding Flex and T-Rex but then just add a different dog, which is not Flex or T-Rex. And it's already liked by default because I think we had set the likedBy to user number one and so it should show up here as well.
Introduce API Persistence1:28
because I think we had set the likedBy to User number one and so it should show up here as well. But if I refresh the page it will be gone. It's not here and not there because we haven't done anything with the API or the database. We literally just set some puppies in our local state. So it's like pretend form submission. We are now going to change this. So in my Laravel API I do have a name point.
We are now going to change this. So in my Laravel API I do have a name point to create a new Puppy. This might not be the optimal code, but basically we are expecting a name trait and imageUrl fields and then this is going to store the image locally, provide a URL for it and return a PuppyResource with the new Puppy just like we had before in the toggle, like
with the new puppy just like we had before in the toggle, like where we would receive the same new puppy or updated puppy resource. And so in other words, we need to submit a form to the react backend that tests /api/puppies and it's gonna be a POST request. We have a fake little sleep once again because it's so snappy fast locally. And so if we have these three fields
because it's so snappy fast locally. And so if we have these three fields that correctly validates, this should work. Alright? And remember that for this form we had used React form actions which has some niceties. And so instead of doing all that here, we want here to hit the API endpoint and hopefully receive the new puppy that we can merge with the existing puppies.
Create POST Query Helper2:46
and hopefully receive the new puppy that we can merge with the existing puppies. I'll comment that out as well. And I think that instead of doing the fetch requests here, I'll go in my queries file like we've done for the getPuppies and toggle like status and hopefully copilot is going to help us follow the same approach that we had. So let's actually try to ask copilot, make a POST fetch request to the /
So let's actually try to ask copilot, make a post fetch request to the /api/puppies and points. We'll just say that and we'll fill in the blanks hopefully from the information it has above on the file. It'll do quite nice. I will accept it and we can review it together. Alright, let's take a look. Uh, so first thing is I want to pass a formData which I can type as formData.
Uh, so first thing is I want to pass a form data which I can type as form data. So let's change it here to form data. All right, next. Okay, we have a try-catch. We try to fetch the react backend test/api/puppies correct method post, correct. Uh, so here the headers content-type technically because we have a file upload in the form, it should be another type which is multipart/form-data.
it should be another type which is multipart/form-data. But believe it or not, the react form action will also determine that we have a file upload. And so set the content type to multipart/form-data for us. So here I can completely remove that line and I will show you the request headers when we submit the form. Next, because we passing the form data, I don't think we need to string justify it.
Next, because we're passing the form data, I don't think we need to string justify it. And then if we don't have an okay in the response, we're going to await the error data and throw it. Otherwise, we destructure the data from the response when we have an okay, so this is exactly the same pattern that up here. And so we return that data, which is hopefully our new puppy, and if there's an error we catch it and then we throw it so we can do something if we need.
and if there's an error we catch it and then we throw it so we can do something if we need to with an error boundary. All right, that's looking pretty good to me. And so now we are going to update all of this. Let's try to receive our new puppy by awaiting the createPuppy function that we've just created and pass it the formData. And so we are hoping in the happy path that we get the data and then we get this returned.
And so we are hoping in the happy path that we get the data and then we get this returned. And on the other side, on the API side, you can see that we return this new Puppy resource for one Puppy. So that's what we are expecting uh, to be in that data. And remember we destructure it because it's wrapped in a data key in the API. Alright, so we should be good. If the scenario is good, we get the new Puppy and if not, it's going to throw an error.
If the scenario is good, we get the new puppy and if not, it's going to throw an error. So assuming we do have the new puppy, we can exactly like this this time it's very straightforward set puppies by spreading the existing puppies, we do receive the puppies here in the component. So we just spread those and then we add the new puppy at the end and let's go in our app and see if we were able to one shot it.
Test Persisted Submission5:46
and let's go in our app and see if we were able to one shot it. So I will refresh the page. Oh by the way, I've done tiny CSS updates to a loader so it's not in the box. I feel like it looks a bit better like this. But now I'm going to scroll down and we are going to try to add Skatey loves skate boarding and you probably know what picture I'm going
skate boarding and you probably know what picture I'm going to select that one. And let's see what happens when I click at puppy click adding in one shot. And this has been added to the API, this is not just in our local state. So if I hard refresh the page and scroll back down, it is going
So if I hard refresh the page and scroll back down, it is going to have persisted this puppy well and truly leaves in the database. So if I refresh the database, there's a sc. Alright, let's open the network tab and we are going to do another one. Let's call this one Beachy Loves the beach. We are going to try find a beachy picture. I'm sure there is. Look at the guilt in the
We are going to try find a beachy picture. I'm sure there is. Look at the guilt in the eyes, sorry. Uh, find a beach image. Ah, there we go. They are going to the beach. So when I submit here, it's going to do a network request to the API endpoint a POST request. So let's take a look at this submit adding beachy and you can see here are requests. So let's squeeze that a little bit.
and you can see here are requests. So let's squeeze that a little bit. And if I scroll down to the request headers, you can see that here the content type was set to multipart/form-data. Uh, and if we look at the payload, this is what it looks like. React form actions did it for us. So we submitted the form with these fields, which is what the API expects. And so then in the response we received our new puppy.
Handle Validation Errors7:30
what the API expects. And so then in the response we received our new puppy wrapped in a data key. And so everything is going according to plan. Okay, but that almost went too well here. So let's sabotage our form just to have an error so we can talk about it. So I will go down in my form and change the trait name to vibe once again. And so what I'm expecting is when we submit the form,
and change the trait name to vibe once again. And so what I'm expecting is when we submit the form, the trait field is not validated and so the request fails. So let's go and try it. Sleepy loves the hammock, is that how you spell it? And we are going to go find that hammock picture there. So let's have the network tab open. Alright, and we are going to submit a form and the whole app exploded. And if we look at the request,
and the whole app exploded. And if we look at the request, it said like we expecting the trade field is required. Remember in the previous lesson we've talked about error boundaries and how you can wrap a chunk of your app into an error boundary and then the error is scoped to this level. So let's say that we could wrap our form here with an error boundary. So I will add the closing tag after the closing form
with an error boundary. So I will add the closing tag after the closing form and we should have an issue here because errorBoundary wants a fallback. And remember you can have the fallback render which gives you the error and actually I'm not quite sure exactly what's in the error. So let's json that string it and I'll turn this into a pre tag. Alright, so we wrapped our form the entire form into an
and I'll turn this into a pret tag. Alright, so we wrapped our form the entire form into an ErrorBoundary component with a fallback that should tell us what happens. So let's try one more time. I'll put Gish here, it doesn't matter. We should have a trait slash vibe issue and so submit. Very cool, that's much better. Our form has disappeared but it's because we are throwing an error
Our form has disappeared but it's because we are throwing an error and kinda replacing the render tree with that. So lemme try to also break the name field by saying names and I will resubmit some gibberish. Once again, let's pick another photo that we haven't seen yet. Really like this one. Look at the tongue. All right, so I'm going to submit that. And so you can imagine that instead
All right, so I'm going to submit that. And so you can imagine that instead of throwing an error if the response is not okay, we could have an error state in our form and then display uh, the errors under the form or even under the fields here. If the response is not okay, we are throwing the error data. But here now where we catch it instead of throwing it again, I think we should return that error. So now uh, new puppy named sort of doesn't make sense.
I think we should return that error. So now uh, new puppy named sort of doesn't make sense because it can be either a new puppy or an error. So let's try to call this response and actually this is where this data wrapper can be useful. So instead of the structuring the data key, I'm just going to get the data and return it in this case so that I can check here if response that data and then I can set the puppies to that and have response that data here.
Show Field Error Messages10:35
and then I can set the puppies to that and have response that data here. In other words, I only update the puppies if we did get the data that we expect. And here let's console, that's log the response. So I don't know if it's the best implementation, but let's try something real quick. const error setError equals useState. Uh, I won't type it and default it to an empty object. And so here instead of logging the response, we can go if
Uh, I won't type it and default it to an empty object. And so here instead of logging the response, we can go if response errors, we are going to set errorResponse that errors to place these errors object in our error here. And so with that, I feel like we can go and decorate our form fields to show the errors specifically if it's related to that field. So for the name field, does the error have a name?
So for the name field, does the error have a name? If yes, let's have a yep. And you need some types here to make it a little bit more robust and clean and probably abstract a ErrorComponent. But just as a proof of concept, let's try to also make the trait field. So here we're gonna check for trait. So let's take a look if that works.
So here we're gonna check for trait trait. So let's take a look if that works. I'll pass some gibberish here and pick any photo. And when I try to add the puppy, check this out. So this ad hoc implementation might not be the best solution. For example, you can see the form has been reset by react form actions. Uh, but typically once again you would use a library for something like this.
Uh, but typically once again you would use a library for something like this. There's react-hook-form, there's 10-stack-forms and libraries that really make things super easy to get the validation and their error messaging. But what we've done here hopefully shows you how you can manipulate react code and states and make things happen like you need. All right, I'll bring the field names to the correct values and I'll get rid of my little demo.
All right, I'll bring the field names to the correct values and I'll get rid of my little demo of the form validation error messaging. And we'll submit one more time. The last pop, leaky loves leaking and you know which picture? I'm going to choose that one definitely. And adding leaky bam. Let's give a like to these nice three new additions and verify that they showed up at the top.
Let's give a like to these nice three new additions and verify that they showed up at the top. And they sure did. All right, our new puppy form is working and our app is starting to feel pretty cool. Now I keep mentioning that in the real world you would use a library here in the library there. So in the next lesson, let's talk about the bigger picture and what sort of landscape goes around React.
So in the next lesson, let's talk about the bigger picture and what sort of landscape goes around React.
