What you will learn

Today we will working on the comment feature of CoderBook

Requirements

Our posts and comments are closely related. A post can have many comments, and a comment must belong to a Post. The following contrived schemas can represent their relation.

Post Schema

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const postSchema = Schema(
  {
    comments: [{ type: Schema.Types.ObjectId, ref: "Comment" }],
    body: { type: String, unique: false, default: "" },
    owner: {
      ref: "User",
      required: true,
      type: Schema.Types.ObjectId,
    },
  },
  {
    timestamps: true,
  }
);

const Post = mongoose.model("Post", postSchema);
module.exports = Post;

Comment Schema

const commentSchema = Schema(
  {
    reactions: { type: Array },
    body: { type: String, unique: false, default: "" },
    post: { ref: "Post", required: true, type: Schema.Types.ObjectId },
    owner: {
      ref: "User",
      required: true,
      type: Schema.Types.ObjectId,
    },
  },
  {
    timestamps: true,
  }
);

Now for commenting on a post.

We need to add state to the CommentForm to collect the body of the comment, then send that data to our API after the user submits.

import React, { useState } from "react";
const [comment, setComment] = useState("");
onChange={(e) => setComment(e.target.value)}
import { useDispatch } from "react-redux";
const dispatch = useDispatch();
const onSubmit = (e) => {
  e.preventDefault();
  dispatch();
};
onSubmit = { onSubmit };

Think about where we should look to find the actions we need to dispatch...

We don't have any redux actions related to comments...

This file will contain all the logic to making requests to our API.

import * as types from "../constants/comment.constants";
import api from "../api";
export const CREATE_COMMENT = "POST.CREATE_COMMENT";
export const CREATE_COMMENT_SUCCESS = "POST.CREATE_COMMENT_SUCCESS";
export const CREATE_COMMENT_FAILURE = "POST.CREATE_COMMENT_FAILURE";
const createComment = (postId, body) => async (dispatch) => {
  dispatch({ type: types.CREATE_COMMENT, payload: null });
  try {
    const res = await api.post(`/posts/${postId}/comments`, {
      body,
    });
    dispatch({
      type: types.CREATE_COMMENT_SUCCESS,
      payload: res.data.data,
    });
  } catch (error) {
    dispatch({ type: types.CREATE_COMMENT_FAILURE, payload: error });
  }
};
export const commentActions = {
  createComment,
};
export * from "./comment.actions";
import { commentActions } from "../../redux/actions";

The function createComment() needs two arguments...

dispatch(commentActions.createComment());
<CommentForm postId={props._id} />
const CommentForm = (props) => {
  const onSubmit = (e) => {
    e.preventDefault();
    dispatch(commentActions.createComment(props.postId, comment));
  };
};

We now see if we create a comment our API responds with a 404... Why...?

We haven't defined a POST route for /posts/:id/comments.

router.post(
  "/:id/comments",
  authMiddleware.loginRequired,
  postsController.createComment
);

We create a comment given the data from the front end. We then update the post's comments with the new comment's id. Then we collect the comments of the post and send it back.

postController.createComment = async (req, res) => {
  const comment = await Comment.create({
    ...req.body,
    owner: req.userId,
    post: req.params.id,
  });

  const post = await Post.findById(req.params.id);
  post.comments.push(comment._id);

  await post.save();
  await post.populate("comments");
  await post.execPopulate();

  return sendResponse(res, 200, true, { post }, null, "Comment created!");
};
const Comment = require("../models/Comment");

We're now able to create comments... incredible...