Today we will working on the comment feature of CoderBook
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.
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;
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.
STEP 4
or look inside of ./client/src/components/Post/Post.js
.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.
useState()
hook.import React, { useState } from "react";
comment
state var.const [comment, setComment] = useState("");
onChange
prop of the Form.Control
.onChange={(e) => setComment(e.target.value)}
import { useDispatch } from "react-redux";
dispatch()
in the body of CommentForm
.const dispatch = useDispatch();
onSubmit()
which will be invoked when the form submits.const onSubmit = (e) => {
e.preventDefault();
dispatch();
};
onSubmit()
to the form's onSubmit
prop.onSubmit = { onSubmit };
./client/src/redux/actions
.We don't have any redux actions related to comments...
comment.actions.js
in ./client/src/redux/actions
.This file will contain all the logic to making requests to our API.
import * as types from "../constants/comment.constants";
import api from "../api";
./client/src/redux/comment.constants.js
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,
};
./client/src/redux/actions/index.js
export * from "./comment.actions";
commentActions
into our Post
component.import { commentActions } from "../../redux/actions";
dispatch()
The function createComment()
needs two arguments...
dispatch(commentActions.createComment());
postId
to the comment form from the Post
body.<CommentForm postId={props._id} />
CommentForm
and send it along with comment
to createComment()
.const CommentForm = (props) => {
const onSubmit = (e) => {
e.preventDefault();
dispatch(commentActions.createComment(props.postId, comment));
};
};
./server/routes/posts.api.js
.We haven't defined a POST
route for /posts/:id/comments
.
router.post(
"/:id/comments",
authMiddleware.loginRequired,
postsController.createComment
);
createComment()
in the controller.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!");
};
Comment
as we use it.const Comment = require("../models/Comment");