React and Rails: Using Active Storage to update a user's profile picture
Tags: rails, react
Published: 2022-08-24 23:25:00

Ever heard of this thing called FaceBook? Every once in a while we snap a pic that features our good side so much we just have to put it on display and make it our profile picture. When you hit that "Edit Profile Picture", it opens upon a window on our local machine to upload a file, bypassing the need to utilize a web URL from something like Imgur. I'll be walking you through how to do this using active storage via a patch request to a user in our database. This guide assumes a few things: You are using React front-end, Rails back-end, and have a database with a User that has a profile-picture type column to hold the file (we can add this last one if you don't). If those three things check out for you, then let's get started.

## Setting up Rails

The first thing we need to do is install Active Storage. In your terminal, run this command:

`rails active_storage:install`

This will create some tables that handle most of the leg work for you. Check your migrations, you should see something like this:

![migrations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ter78yny4s4q8d1ukik9.png)
 
All we need to do from here is migrate the changes.

`rails db:migrate`

For my project, I created a column in my User table called "avatar", with a data type of a string. The column can be called anything, but I suggest using something that indicates it should be some sort of file. If you check your schema, you'll see the migrated tables reflected from your migration.

![schema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzn6d7cty8s8ajkpgxqb.png)
 
## Serializer

All you need to do here is replace the word avatar in my serializer with whatever column in your user table is supposed to hold the image file.

![serializer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gce58zwbz6u4boovfh07.png)

Do not forget to `include Rails.application.routes.url_helpers`!
 
## Model association macro

In your user model, create a **has_one_attached :avatar** association. 

![model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zrmead9hcj54bkuv1472.png)

## Controller

Your controller can stay pretty much the same, just make sure in your params method you include the image attribute from your User table.

![update](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qrmbbigdr5lunzsb26k.png)

![params](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acypvx4hylomc8xafgzx.png)

## What if I don't have an image in my user table?

`rails g migration addImageToUsers image`

`rails db:migrate`

This will create a new table that adds the image column to our user table, and updates the schema when we migrate these changes.

## Setting up React
 
For my project, I used MUI for styling. It doesn't change too much if you aren't using MUI, but this guide will show you the React setup with MUI.

## State

This is all you need to hold the data when it comes to a patch request:

![state](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xa0sjjddzjokfhdjvz2f.png)

## Form

![form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q05n5es8dcz4b53d3m6a.png)

There's a few things going on here for the form, so let's go line by line.

- Line 131: Create the form element.
- Line 132: This grid item holds the button that we use to select the file from our local machine.
- Lines 133-140: The text in this button will change depending on if an image has been selected using a ternary expression. If the image has been selected, display the name of that image. If not, display "Change Avatar"
- Line 136: Input. Here we declare what type of input it is (136), what kind of file it can accept (137), the fact that it should be hidden (138), and the onChange function that will change our state variable to the image that we have selected (139).
- Line 142: Here is another ternary. If an image has been selected, then we will display the submit button that is responsible for running the function that makes our patch request. If no image is selected, no button will appear.

## Making the request

Now it's time to write out our handleSumbit function that is responsible for carrying out our request. You will need a current user object in the component you're making request so Rails knows which user you are sending the patch request to.

![patch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdbve3ap0sqmhamivs45.png)

In our `new FormData()` variable, we're setting the value of our image column on the database to the state variable holding the file, in this case a user's avatar. The string in our append represents what's in our schema, and the variable next to it represents the file that's been uploaded to our state variable.

For debugging purposes, if you want to check to see if a file has been uploaded before making the patch request, `console.log(avatarData)` **outside** of the patch request. When the file has been uploaded (but not sent), in your browser console you should see a file object holding the information about the file being uploaded.

## That's it!

This is all you need to get Active Storage up and running. Having said that, getting Active Storage to run properly the first time you've learned it is... challenging. So here's some resources you can use to clarify any potential problems:

https://edgeguides.rubyonrails.org/active_storage_overview.html

https://dev.to/jblengino510/uploading-files-in-a-react-rails-app-using-active-storage-201c