I am stuck trying to do this. I have tried multiple different methods and I haven't been able to crack this. The User should see their Posts when they open the Profile. I have added the PostSchema and User schema along with the routes and actions I have used. Please do correct me wherever I am wrong. I have also added the client side to know how I should integrate this.
PostSchema
import mongoose from 'mongoose';
const postSchema = mongoose.Schema({
title: String,
message: String,
name: String,
creator: String,
tags: [String],
selectedFile: String,
likes: {
type: [String],
default: [],
},
disLikes: {
type: [String],
default: [],
},
comments: {
type: [String],
default: [],
},
createdAt: {
type: Date,
default: new Date()
},
});
const PostMessage = mongoose.model('PostMessage', postSchema);
export default PostMessage;
UserSchema
import mongoose from 'mongoose';
const userSchema = mongoose.Schema({
name: {type: String, required: true},
username: {type: String, required: true},
emailId: {type: String, required: true},
phoneNumber: {type: String, required: true},
password: {type: String, required: true},
imageUrl: String,
id: {type: String},
})
export default mongoose.model("User", userSchema);
Controllers (getUserPosts is what I am trying to use)
import mongoose from "mongoose";
import PostMessage from "../models/postMessage.js";
import User from "../models/user.js";
export const getPosts = async (req, res) => {
try {
const posts = await PostMessage.find().sort({ _id: -1 });
res.status(200).json({ data: posts});
} catch (error) {
res.status(404).json({ message: error.message});
}
};
export const getPost = async (req, res) => {
const { id } = req.params;
try {
const post = await PostMessage.findById(id);
res.status(200).json(post);
} catch (error) {
res.status(404).json({ message: error.message});
}
};
export const getUserPosts = async (req, res) => {
const creator = req.userId;
try {
const userPosts = await PostMessage.find(creator).sort({ _id: -1 });
res.status(200).json({data: userPosts});
} catch (error) {
console.log(error);
res.status(500).json({ message: error });
}
};
Routes
import express from "express";
import { getPostsBySearch, getPosts, getPost, createPost, updatePost, deletePost, likePost, disLikePost, commentPost, getUserPosts } from "../controllers/posts.js";
import auth from '../middleware/auth.js';
const router = express.Router();
router.get('/search', getPostsBySearch );
router.get('/', getPosts );
router.get('/:id', getPost);
router.get('/userId', auth, getUserPosts);
router.post('/', auth, createPost);
Actions
import { FETCH_ALL, CREATE, UPDATE, DELETE, FETCH_BY_SEARCH, FETCH_POST, START_LOADING, END_LOADING, COMMENT, FETCH_USER_POSTS } from '../constants/actionTypes';
import * as api from '../api';
export const getPosts = () => async (dispatch) => {
try {
dispatch({ type: START_LOADING });
const { data } = await api.fetchPosts();
dispatch({ type: FETCH_ALL, payload: data });
dispatch({ type: END_LOADING});
} catch (error) {
console.log(error);
}
}
export const getPost = (id) => async (dispatch) => {
try {
dispatch({ type: START_LOADING });
const { data } = await api.fetchPost(id);
dispatch({ type: FETCH_POST, payload: data });
dispatch({ type: END_LOADING});
} catch (error) {
console.log(error);
}
}
export const getUserPosts = (userId) => async (dispatch) => {
try {
dispatch({ type: START_LOADING });
const { data } = await api.fetchUserPosts(userId);
dispatch({ type: FETCH_USER_POSTS, payload: data });
dispatch({ type: END_LOADING});
} catch (error) {
console.log(error);
}
}
API
import axios from "axios";
const API = axios.create({ baseURL: 'http://localhost:5000' });
API.interceptors.request.use((req) => {
if(localStorage.getItem('profile')) {
req.headers.authorization = `Bearer ${JSON.parse(localStorage.getItem('profile')).token}`;
}
return req;
});
export const fetchPosts = () => API.get('/posts');
export const fetchPost = (id) => API.get(`/posts/${id}`);
export const fetchPostsBySearch = (searchQuery) => API.get(`posts/search?searchQuery=${searchQuery.search || 'none' }&tags=${searchQuery.tags}`);
export const fetchUserPosts = (userId) => API.get(`posts/${userId}`);
ProfilePage (component)
import React, { useState, useEffect } from 'react';
import {makeStyles} from '@material-ui/core/styles';
import { useLocation } from 'react-router-dom';
import { Container, AppBar, Typography, Grow, Grid, Card, Button, ListItemAvatar, ListItem, List, Paper} from '@material-ui/core';
import { useDispatch, useSelector } from "react-redux";
import UserProfile from './UserProfile';
import UserPosts from '../Posts/UserPosts';
import { getUserPosts } from '../../actions/posts';
import "../../App.css";
import ProfileData from './ProfileData';
import useStyles from "./styles";
const ProfilePage = () => {
const [currentId, setCurrentId] = useState(null);
const [userId, setUserId] = useState(null);
const { post, posts, isLoading } = useSelector((state) => state.posts);
const [user, setUser] = useState(JSON.parse(localStorage.getItem('profile')));
useEffect(() => {
const token = user?.token;
setUser(JSON.parse(localStorage.getItem('profile')));
}, [location]);
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
dispatch(getUserPosts());
}, [userId, dispatch]);
return (
<>
<Grow in>
<Container maxWidth="lg">
<Grid contianer position= "absolute" justify="space-between" alignItems="stretch" spacing={1}>
<Paper className={classes.card} elevation={7}>
<ProfileData />
</Paper>
<div className={classes.buttons}>
<Button className={classes.button}>{user.result.name}'s Posts</Button>
<Button className={classes.button}>Constests entered by {user.result.name}</Button>
</div>
<Grid item xs={12} sm={11}>
<UserPosts setUserId={setUserId}/>
</Grid>
</Grid>
</Container>
</Grow>
</>
)
}
export default ProfilePage
How i am populating my Feed as of now is using this on the client side. Feed
import React from "react";
import useStyles from "./styles";
import { Grid, CircularProgress } from "@material-ui/core";
import Post from './Post/Post';
import { useSelector } from "react-redux";
import { Marginer } from "../Auth/Marginer";
import Formbar from "../FormBar/FloatingButtonForm";
const Posts = ({ setCurrentId }) => {
const {posts, isLoading} = useSelector((state) => state.posts);
const classes = useStyles();
if(!posts.length && !isLoading) return 'No Posts Available';
return (
isLoading ? <CircularProgress /> : (
<Grid className={classes.container} container alignItems="stretch" spacing={8}>
<Marginer direction="vertical" margin="75px" />
<Formbar/>
{posts.map((post) => (
<Grid key={post._id} item xs={12} sm={12} >
<Post post={post} setCurrentId={setCurrentId}/>
</Grid>
))}
</Grid>
)
);
}
export default Posts;
Reducers
import { FETCH_ALL, CREATE, FETCH_POST, UPDATE, DELETE, FETCH_BY_SEARCH, START_LOADING, END_LOADING, COMMENT, FETCH_USER_POSTS } from '../constants/actionTypes';
export default (state = { isLoading: true, posts: []}, action) => {
switch(action.type) {
case START_LOADING:
return {...state, isLoading: true};
case END_LOADING:
return {...state, isLoading: false};
case DELETE:
return {...state, posts: state.posts.filter((post) => post._id !== action.payload)};
case UPDATE:
return {...state, posts: state.posts.map((post) => post._id === action.payload._id ? action.payload : post)};
case COMMENT:
return {...state,
posts: state.posts.map((post) => {
if(post._id === action.payload._id) return action.payload;
return post;
}),
};
case FETCH_ALL:
return {
...state,
posts: action.payload.data
};
case FETCH_USER_POSTS:
return{...state, posts: action.payload};
case FETCH_BY_SEARCH:
return {...state, posts: action.payload };
case FETCH_POST:
return {...state, post: action.payload};
case CREATE:
return {...state, posts: [...state.posts, action.payload]};
default:
return state;
}
}
