How to use Active Storage with React Hook — useForm, Redux toolkit and Rails API
By: Iryna Stein
Tags: Full Stack, Active Storage, Rails Api, Redux Toolkit, React Hook

How to use Active Storage with React Hook — useForm, Redux toolkit and Rails API Iryna Stein 6 days ago·7 min read

When I decided to learn how to use Active Storage I didn’t think it would be so difficult to find references for my particular situation. But it was and so I decided to create a little walkthrough on how to use all four of these tools together and successfully send and store files in your backend.

Let’s assume that you already have your backend and front end in place. There are a few more steps you will need to do to get started. If you look over the existing blogs, Active Storage seems like a be pretty straightforward set up. But don’t get fooled, I found that some things are not mentioned which presented a huge problem personally for me. Here, I created a very specific step by step succinct guide on how to initialize your front and backend set up. But please keep in mind that my intention is to point out the particular steps and not to provide detailed references on all the tools used. I safely assume that you are already pretty familiar with Rails, Redux and React.

Setting up Active Storage in Rails.

First, you have to run a command <bin/rails active_storage:install> to generate a migration that will create a few tables to handle your uploads under the hood. You will notice that you now have three extra tables in your schema: active_storage_attachments, active_storage_blobs and active_storage_variant_records. For the purpose of this blog I am not going to dive into details of what these tables do and how they interact with each other, but you can read more about them in Rails documentation.

If you generated rails backend without a minimal flag you should be all good to go.

(Note: Make sure you have gem ‘image_processing’, ‘~> 1.2’ present in your Gemfile).

2. Making sure that you User table doesn’t have a similarly named column.

This is very important and was one huge mistake I made. My users table (just an example of a table associated with a model which will have an image upload functionality) had an “image” column which I thought was necessary for creating an association for Active Storage image record. ABSOLUTELY DO NOT DO THAT. Leave this problem to Active Storage itself. Do not create any additional columns for the models which are associated with the image upload. If you accidentally do create a column or if it already exists you will pretty quickly notice that your code will go into an infinite loop and collapse your stacks in the backend. You can simply remove your existing column by running <bin/rails generate migration RemoveFieldNameFromTableName field_name:datatype>

3. Configuring the Model

In your model which is expecting to have an Active Storage association with an image upload, make sure to add <has_one_attached :image>. The has_one_attached macro is responsible for setting up the upload association, which saves you a trouble of configuring it yourself as I mentioned earlier. This association can have any name: avatar, image, file, upload… just make sure that it is consistent with your params, and any other front-end data. I also added <…dependent: :destroy> since I wanted the uploaded files to be destroyed automatically in case the User is deleted.

4. Allowing image params for “create”

If you are using strict params to create your new User instance (which you should!), make sure to add :image into the allowed params like so:<params.permit(:user_name, :password, :password_confirmation, :email, :image)>

5. Configuring User serializer

Now that we are done with setting up the receiving side, let’s make sure our image is being stored and returned properly when we are rendering/sending the response. If you are using a serializer for your Model, go ahead and add :image as an attribute. Additionally, include the following code inside your serializer:

User serializer

These two methods (image and image_url) will store the uploaded file into your database and add the image attribute to the User’s hash. And again, I won’t be diving into the detailed explanation of the above methods, you can read and learn more about them here.

6. Configuring environment files

Inside all 3 environment files (config/environments/development.rb, production.rb and test.rb) you will notice the active_storage support and different configurations. Drop down to where you see the <config.active_storage.service = :local>. There are different options and configurations depending on your deployment platform, but I am going to stick with locally storing files for the purpose of this blog. Add the following code to each one of these files right under the service config line:

Environment files

You can refer to Rails documentation for more information about different types of configs for various platforms (AWS, Google Cloud, Azure etc)

At this point our backend should be all set up for receiving storing and sending back response with an image upload. Let’s move onto the front end now.

7. Appending files with React useForm

Setting up controlled fields can eat up a lot of your time, and so I chose to implement a useForm hook in this project which allowed me to shorten this daunting process and boil it down to a few seconds. However, regardless of how you are gathering your form data, appending the files is going to be a very similar process. The upload input field should look something like this:

File upload input field (useForm)

Where the type=“file” gives you an ability to upload an actual file object. Lets talk about this file object a little bit, so you understand what values you need to send regardless of how your form is initially set up. If you console.log your image input value you will notice a FileList object with various attributes.

FileList object

That’s the object that allows us to capture all the data associated with an uploaded file. You need to parse this object and get the file(s) out of it so to say. Depending on your set up you might need to use e.target.files[0] or data.image[0] (in my case) but the end result and the value that you are getting form it should look something like this:

parsed File

After you parsed the files you need to send them somehow. Here we are going to use the new FormData — special JavaScript Object: const formData = new FormData() and thereafter append all the fields of our existing form to this newly created object. Keep in mind that because FormData is not a regular object but of a special kind, you can’t simply console.log the variable it’s stored to check its current key:value pairs. But it is possible by using a spread operator on it like so: console.log(…formData) — strictly for viewing purposes.

FormData console.log

If you have more than one field in your form you need to append all of them, otherwise you will be missing some info when sending data to your backend.

Appending data to new FormData

That’s it — we grabbed all our fields and ready to proceed with a fetch.

8. Sending formData to the backend

Whether you are suing Redux toolkit Async Thunk or just a regular fetch post it is important to remember that FormData object is a NON_STRINGIFIABLE OBJECT. You need to send it as it comes. Don’t worry if it’s your first time doing it, I was also nervous and had doubts about the receiving end. But it all works out once set up.

Sending fetch POST using Async Thunk

Notice that the body of my fetch drops the JSON.stringify() method and just send the raw formData.

I put a bye bug in my create new User method on a backend to show you what the params and params[:image] look like at this point:

Receiving params on the backend

And that’s sums up the process of setting up your Rails API and React front end image upload and storage. I hope you found this blog useful. For comments and questions please drop me a message below.