Simple Instagram app with Rails 6 API and React. Part 3

In this part, we will be adding comments to our app.

Part 2 is here

First we need to create a comments table in our db. We would then give it a reference to our Pictures table so that each comment can belong to a picture. We also want to keep track of the names of people who leave a comment. We will skip tests in this part.

Let’s start.

Checkout to a new branch. Call it comments. Then create a comments model that has a name and comment column.

rails g model Comment name:string comment:string

Let’s add some validations. A comment should always have a name and a comment and it should belong to a picture

app > models > comment.rb
class Comment < ApplicationRecord
validates_presence_of :name, :comment
belongs_to :picture
end

We also need to update our Picture model

class Picture < ApplicationRecord
validates_presence_of :img_link, :created_by
has_many :comments
end

Next we need a controller for us to get and send data to our model. We probably won’t get through writing all the code for this methods but we will have them in our controller if we decide to further develop the app in the future.

rails g controller comments index show create update destroy

In your routes.rb file, delete the ‘get’ routes generated and add the following. We want to make the comments route off the pictures.

Rails.application.routes.draw do
resources :pictures, only: [:show, :destroy, :index, :create, :update] do
resources :comments, only: [:index, :show, :create, :update, :destroy]
end
end

We forgot to add the reference to the pictures on the comments table. Not to worry. There’s a way around that.

rails g migration add_pictures_to_comments picture:references
rails db:migrate

Check the db >schema.rb and you should see a reference to our pictures on the comments table.

Reference to picture on comments table

Now let’s write our controller actions.

class CommentsController < ApplicationController
before_action :set_picture #find a picture before running any of the actions here
before_action :set_picture_comment, only: [:show, :destroy]

And now for some manual tests.

Fire up Postman once more.

Our comment creation should return the picture, we can then make a GET request to the same route to get the comment.

You can test all the other routes on your own.

Make a commit at this point.

Now to React

We will start by creating a comments component. We want our comments to go under our captions and before our likes.

To avoid Unique key warnings from react, we will install a package called uuid.

npm i uuid --save

In your Comments component you should have the following. I’ll explain with comments inside the code.

# first we import necessary modules/files

Then head back to our pictures component as this is where we would get the pid prop from in order to grab the comments belonging to a picture.

const displayAllPictures = pictures.map(picture => (
<div key={uuid()}>
<PictureCard
key={picture.id}
uName={picture.created_by.toLowerCase()}
displayPicture={picture.img_link}
picture={picture.img_link}
caption={picture.caption}
increaseLikes={this.increaseLikes}
pid={picture.id}
/>
<Comments pid={picture.id} />
<Likes dateCreated={picture.created_at} likes={picture.likes} />
</div>
));

Let’s style our comments. This should be our output after we are done styling.

.ShowComment {
margin-left: 1.1em;
margin-top: 0.2em;
}

Now, our comments should render each time the user hits enter.

Finally, we need a way for the user to delete comments on the click of a button. First import FontAwesome like we did in the Pictures component. Then import the faTrashAlt icon. Use flexbox to style your comment and icon so it looks aligned.

Now add an onClick handler to the icon.

<FontAwesomeIcon icon={faTrashAlt} onClick={this.handleDelete} />

Make sure to bind this handler in your constructor. Now let’s define our handleDelete method. Binding the handleDelete method means that we have to use a currentTarget method to get the id of a specific comment.see below.

handleDelete(e) {
const { pid } = this.props;
const cid = e.currentTarget.getAttribute("cid");

I’m also creating a notification state that gets updated each time a note is deleted. After 3s, the notification messaged is removed.

When a comment is deleted, we instructed our API server to send back the comments belonging to a particular picture. We use this to re-render the DOM by setting the state to those comments. Remember that in react, each time we call setState, the DOM is re-rendered.

Full-stack Engineer. Rails/Node.js/React. I love plantain chips! Reach me at Adebola.dev.