I am making a MERN stack app (Social media app) using redux toolkit as the state management system. My problem is even though my redux state changes in my dev tool according to the order that I want it in ( createdAt latest ), it is not represented in my UI.
Here is my code in the server side.
Post Model
import mongoose from "mongoose";
const { Schema } = mongoose;
const requiredNumber = {
type: Number,
required: [true,'Please add a longitude and latitude'],
};
const postSchema = new Schema(
{
userId: { type: String, required: true },
title: {
type: String,
// required: [true,'Please add a title'],
},
description:{
type: String,
required: [true,'Please add a description']
},
image: [{
type: String,
}],
latitude: {
...requiredNumber, //required
min: -90,
max: 90,
},
longitude: {
...requiredNumber, //required
min: -180,
max: 180,
},
visitDate: {
// required: [true,'Please add a visit date'],
type: Date,
},
airline:{
type: String,
},
//
// name: {
// type: String,
// },
// avatar: {
// type: String,
// },
// Likes ,comments & date of
likes: {
type: Array,
default: [],
},
comments: [{
type: Schema.Types.ObjectId,
ref: 'Comment'
}],
date: {
type: Date,
default: Date.now,
},
shares:{
type: Array,
default: [],
},
// a post may or maynot be a shared post
// sharedPost:{
// type: Schema.Types.ObjectId,
// // required: true,
// ref: "Post",
// }
},
//---------------------
{
timestamps: true,
}
);
// We registered the model with mongoose
var PostModel = mongoose.model("Posts", postSchema);
export default PostModel;
Here is my PostController
export const getTimelinePosts = async(req,res) =>{
try {
const userId = req.params.id;
const currentUser = await UserModel.findById(userId);
const userPosts = await PostModel.find({ userId: userId });
const friendPosts = await Promise.all(
currentUser.following.map((friendId) => {
return PostModel.find({ userId : friendId });
})
);
res.status(200).json(userPosts.concat(...friendPosts).sort((a,b)=>{
return b.createdAt - a.createdAt; //ordered
}))
} catch (err) {
res.status(500)
throw new Error(err)
}
}
I have a route in my PostRoute
router.get("/:id/timeline", getTimelinePosts)
On the client side I have a postService in my feature folder (redux) which hits the route using axios and returns the data(posts).
// Get timeline posts for user
// @route GET /posts/id/timeline
const getTimelinePosts = async (userData) => {
const response = await axios.get(API_URL + `${id}/timeline`)
return response.data
}
This is how I have my slice set up.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import postService from './postService'
const timelinePosts = []
const initialState = {
user: user ? user : null,
timelinePosts: timelinePosts ? timelinePosts : null,
isError: false,
isSuccess: false,
isLoading: false,
message: '',
}
// Get timeline posts for user
export const getTimeLinePosts = createAsyncThunk(
'post/timelinePosts',
async ( _ , thunkAPI) => {
try {
const user = thunkAPI.getState().auth.user // getting user from auth state
return await postService.getTimelinePosts(user)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
// Create post
export const createPost = createAsyncThunk(
'post/createPost',
async (postData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token
return await postService.createPost(postData,token)
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
return thunkAPI.rejectWithValue(message)
}
}
)
In my extra reducer:
// getTimeLinePosts
.addCase(getTimeLinePosts.pending, (state) => {
state.isLoading = true
})
.addCase(getTimeLinePosts.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.timelinePosts = action.payload
})
.addCase(getTimeLinePosts.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
state.timelinePosts = null
})
// createPost
.addCase(createPost.pending, (state) => {
state.isLoading = true
})
.addCase(createPost.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.timelinePosts = [action.payload, ...state.timelinePosts]
})
.addCase(createPost.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
})
Even though initially after loading my posts show up in the order intended, after creating a post the order does not persist and my new created post shows up at the bottom of the feed.
Here is the AllPosts (post generator which feeds my timeline)
import { useSelector, useDispatch } from "react-redux";
import { getTimeLinePosts,createPost } from '../features/post/postSlice';
import { useEffect } from "react";
const AllPosts = () => {
// redux
const dispatch = useDispatch();
const { user } = useSelector(
(state) => state.auth
)
let { timelinePosts, isLoading } = useSelector((state) => state.post);
useEffect(()=>{
//running the api call on first render/refresh
dispatch(getTimeLinePosts(user.user._id))
},[])
// const sortedTimelinePosts = arrayForSort.sort(function(a,b){
// // console.log(a.updatedAt)
// // console.log(b.updatedAt)
// return b.createdAt.localeCompare(a.createdAt);
// })
//\
return (
<div>
{timelinePosts ? timelinePosts.map((post, id) => {
return <Post data={post} key={id} />;
}):null}
</div>
)
}
export default AllPosts
If I use the sortedTimelinePosts (which I commented out) to populate my post it does not work.
When I create the post I get the timeline posts again to rehydrate my state in redux store. Even though the changes reflect in the DevTools, it fails to show in my UI.
// handle post upload
const handleUpload = async (e) => {
e.preventDefault();
// post data
if(data.description && newPlace.lat && newPlace.long){
const newPost = {
userId: user.user._id,
title: data.title,
description: data.description,
latitude: newPlace.lat,
longitude: newPlace.long,
};
console.log(newPost)
// create post
dispatch(createPost(newPost,user))
// rehydrate store
dispatch(getTimeLinePosts(user.user._id));
setShowModal(false)
resetShare()
// Bandage fix
window.location.reload()
}
Currently I am refreshing page using 'window.location.reload()' as my UI is not reacting to changes in the store. Can anyone please help?