React File Uploads to Rails
By: Ignas Butautas
Tags: Files, Coding, React, Upload, Rails

Going forward, your database is pretty set up to take in file uploads from users, but there’s one more step that you need to take and I’ll touch on that later. On your frontend you will find a few headaches here, there are many different ways that you can send files to the backend, and I’m only going to show the easiest way (in my opinion.)

To start off, you will need to install ‘axios’ (which is as simple as ‘npm install axios — save’ in your terminal) and then on the top of your React file you’ll want to import axios like so:

import axios from 'axios'

Let’s say you have a User signup page where a user has to fill out a bunch of information. In React.js you will just have a state that holds all of this information:

state = { username: '', password: '', fname: '', lname: '', age: '', email: '', bio: '', image: '', errors: [] }

As the user fills out the information the state gets updated to hold the users input. The input fields in your form will stay the same for your cookie cutter information, but when it comes to the HTML tag for file uploads it will look something like this:

<label>Profile Image</label> <input type="file" accept="image/jpeg" /// for images onChange={this.handleFileUpload} /> accept=".mp3,audio/*" /// for sound files \\\\\\\\ File upload function will look like this \\\\\\\\\\\ handleFileUpload = (e) => { this.setState({ image: e.target.files[0] }) }

When uploading files, it will be stored in an array. Since we are only accepting one file upload, it will always be at the zeroth position.

Your ‘handleSubmit’ function will do all of the heavy lifting of packaging up your state and sending it off to your backend. To send files over to the backend you need to use FormData. Soo, your function will have something that look like this in it:

handleSubmit = (e) => { e.preventDefault() const formData = new FormData() for (const property in this.state) { formData.append( property, this.state[property] ) }

Let’s break this down, since there’s a lot going on.

We are preventing default (so that the page doesn’t refresh after sending a form.) We are creating new FormData, which pretty much looks like an empty object. To take a deeper dive checkout this link. This is my gift to you. This for … in loop neatly packages up everything in your state with the key value pairs of your state. ( username: ‘Iggs’) including the file the user uploaded. FormData is quite difficult to work with since, from my experience there is no way to peer inside of it. Console.log will return an empty object, debugger will only view inside an empty object (when it very clearly holds all of the information from the state.) So in other words it’s a lot of guessing and checking if it’s your first time working with FormData.

Now the only thing that’s left to do is, sending this formData to the backend:

axios.post("http://localhost:3000/users", formData)

Boom, ezpz. Now going back to the backend you controller needs to be able to accept this file upload. But it’s no sweat because Rails is the best. In your controller that you are trying to attach this file to, you’ll need to permit them in your params:

params.permit(:username, :password, :fname, :lname, :age, :email, :bio, :image)

That’s about it.

“But Ignas, how do I send this information from the backend to the frontend?”

Thanks for asking, that isn’t to hard either if you’ve added the serializer gem mentioned before. Your serializer file will look something like this:

class UserSerializer < ActiveModel::Serializer attributes :id, :email, :fname, :lname, :bio, :age, :username, :image

include Rails.application.routes.url_helpers def image rails_blob_path(object.image, only_path: true) if object.image.attached? end end

Now when you fetch information about users from the backend it will send everything shown above that ‘attributes’ points to. The ‘image’ that will be sent down will be a super long string that you need to plop onto a url. Like so:

const imageUrl = "/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBc3dCIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--4b10997f040ab8eab876424dadcae0c4cd8caa14/20181224_113217.jpg" const link = `http://localhost:3000${imageUrl}` <img src={link}>

That’s about it. Thank you for taking the time to read this, I really do hope this helped out.