Edit User-Owned Cleaning Resources with React and FastAPI

undraw svg category

Welcome to Part 18 of Up and Running with FastAPI. If you missed part 17, you can find it here .

This series is focused on building a full-stack application with the FastAPI framework. The app allows users to post requests to have their residence cleaned, and other users can select a cleaning project for a given hourly rate.

Up And Running With FastAPI

In the previous article in this series, we started consuming cleaning resources sent by our FastAPI backend and gave authenticated users the ability to create new cleaning jobs from our React frontend. We also modified our backend to provide user info along with any cleaning jobs returned from our endpoints.

This time around, we’ll briefly cover allowing users to edit cleaning resources they’ve posted, as long as they haven’t already accepted an offer.

Adding An Edit Cleaning Route

Start by adding a new component to the project.

Then add just a tiny bit of code…

CleaningJobEditForm.js

And export it.

components/index.js

Then let’s go ahead and refactor a few components.

We’re going to update the routing configuration so that /cleaning-jobs/2 displays the cleaning resource in a card, but /cleaning-jobs/2/edit pulls up a form to edit that resource as long as the currently logged in user is the owner.

First, head into the CleaningJobsPage.js component and update it like so:

CleaningJobsPage.js

We’ve added a wildcard operator (*) at the end of our :cleaning_id route to match any suffix appended to the id of the resource. Doing so allows us to nest a router inside the CleaningJobView component and display an update form when the user navigates to the /edit route.

Open CleaningJobView.js and make the following changes:

CleaningJobView.js

We import the Router and Route components along with the useNavigate hook from react-router-dom. On top of that we import the CleaningJobEditForm component that we just created. At the bottom of the component, we add our nested router that map CleaningJobCard to the default / path and CleaningJobEditForm to the /edit path. Anything else gets routed to NotFoundPage.

If we create a cleaning listing and then manually navigate to /cleaning-jobs/{id}/edit, we’ll see our dummy component in action. It would be much nicer if we could allow the user to navigate to that edit form themselves.

Let’s do that now.

CleaningJobView.js

Much better. We’ve added a few new things.

At the top of the file we import EuiButtonEmpty and EuiButtonIcon and use both of these components inside an additional router. When the user is at the /cleaning-jobs/{id}/edit route, they’ll see a back button that navigates to the /cleaning-jobs/{id}/ path.

If they’re currently viewing the default route, they’ll see an edit icon that takes them to the edit form - but only if they are the user that owns the cleaning resource in question. We determine that ownership with the new user prop that is passed to our CleaningJobView component from the redux state tree.

Now it’s time to actually create our edit form.

Creating The Edit Cleaning Job Form

For the sake of time, we’re going to start by copying everything from the CleaningJobCreateForm component into our CleaningJobEditForm component and then make a few minor adjustments.

CleaningJobEditForm.js

The main changes we’ve made involve the props we’re passing our form component from redux and how we initialize our form state.

Because we have access to current cleaning job, at the top of our component we can initialize each of the form fields to the values sent from our FastAPI server. We use destructuring syntax to extract the appropriate properties from the cleaningJob prop and pass them directly to our initial form state.

The submit button has also been changed to include a new save icon, and the isLoading prop uses a new flag - isUpdating - to determine when to show a spinner inside the button. Our handleSubmit function is now using a yet-to-be-created updateCleaning action creator function from redux that will make the necessary HTTP request to the backend. We’ll get to both of these in a minute.

Observant readers will recognize a code smell here. We have introduced quite a bit of code duplication.

If we insisted on making this repo as clean and DRY as possible, we could instead create a CleaningResourceForm component and have both the CleaningJobEditForm and CleaningJobCreateForm leverage it. We’ll leave that as an exercise for the reader this time around and stick to our current approach.

One more thing we should address before moving on to our redux files. Even though we aren’t linking directly to the edit form if a user doesn’t own this cleaning resource, nothing prevents the user from technically manually navigating to that path. Our backend permissions dependencies would still prevent the user from making any updates, but that behavior is still undesirable.

Any easy remedy to that problem is to create a wrapper component to prevent unauthorized access.

The PermissionsNeeded Wrapper Component

Create a new file called PermissionsNeeded.js.

And then add the following code to the file:

PermissionsNeeded.js

Nothing too fancy here. We take in an isAllowed flag and element component as props (using the same syntax that the Route component from react-router-dom does). If the user isn’t authorized to view this content, we render an "Access Denied" prompt. Otherwise, we return the element.

Go ahead and export that component.

components/index.js

Then incorporate it into the CleaningJobView component like so:

CleaningJobView.js

There we go.

Try it our yourself. Log in and navigate to a cleaning resource that the authenticated user doesn’t own. Tack /edit on to the end of the route and see our permissions wrapper at work.

All that’s left to handle is the redux piece of the puzzle.

Adding An Update Cleaning Action Creator

First edit the redux/initialState.js file with the isUpdating flag:

redux/initialState.js

Then, open up the redux/cleanings.js file and add the following:

redux/cleanings.js

We start by defining three action type constants to represent our application at different stages of the update cleaning process. Then, we use each one to modify the isUpdating flag and store any error returned from our server. Notice how our reducer doesn’t change the currentCleaningJob at all, even when an updated job is returned for UPDATE_CLEANING_JOB_SUCCESS? The reason is that we handle updating state in our updateCleaningJob action creator.

At the bottom of the file, we define our Actions.updateCleaningJob function and use it to make an HTTP PUT request to the /cleanings/${cleaning_id}/ path, sending whatever updates were entered into the CleaningJobEditForm. We also attach an onSuccess callback that refetches the modified cleaning job from our FastAPI server if all goes well.

So once a user decides to edit any of the attributes on a cleaning resource they own, our frontend makes the PUT request with the appropriate updates and requests the modified cleaning job afterwards.

Try it out! It should work nicely.

But something else should catch our eye. Are the multiple requests necessary? Couldn’t we avoid a roundtrip to the server by simply updating the currentCleaningJob with the result of our HTTP PUT request? Sure we could! Let’s see how that would look instead.

redux/cleanings.js

The advantages to this approach is that we only make a single api call to our backend and our updateCleaningJob function is simplified. The tradeoff is that our cleaningsReducer is now clunkier.

In that reducer we use a reduce function to compose an object of updates that are spread over the currentCleaningJob attribute. When composing that object in our reduce function, we skip over the owner attribute in the response. We do this because the cleaning resource returned from our FastAPI server for PUT requests isn’t populated with the full owner profile. If we updated this attribute as well, we’d overwrite all owner profile data with that user’s id. In the future, we could populate the response from all PUT requests on the FastAPI side to prevent the need for excess reducer code.

Either way works, so it’s nice to have both options available to us. We’ll be sticking with the latter approach to avoid unnecessary API calls for the time being.

Check it out on Code Sandbox

phresh-frontend-part-6-editing-a-user-owned-cleaning-resource

Try it out again. If everything is in order, we should be see our new feature working as expected.

Look at us go!

Wrapping Up and Resources

We’ve spent the entirety of this post in our frontend repo, adding more routing and implementing edit functionality. The addition of the PermissionsNeeded component also provides us with a way to prevent unauthorized users from accessing resources they’re not allowed to.

With that out of the way, it’s time to move on to our next challenge. We’re almost ready to start building a feed of potential cleaning jobs for users who want to offer their services. However, since we already have an “Offer Services” button on each cleaning job card, it probably makes more sense to first attach the appropriate functionality there. So we’ll do that next before moving on to more backend work.

Tags:

Previous Post undraw svg category

Consuming a FastAPI Backend from a React Frontend

Next Post undraw svg category

Creating and Viewing Job Offers With React and FastAPI