'why is an array fetched from backend not in the same order in redux store (react app)?
In my React app, i am fetching an array of posts from a backend api (nodejs/SQL DB). I am using redux for the frontend, so i thought it would be a good idea to sort the posts on the backend and send them to the frontend (sorted by id, from latest to oldest). Then, the array of posts gets stored in my redux store.
It's working fine, but i am confused because when i check the store, the posts are not ordered anymore, or rather: the same 4 random posts always get "pushed" to the top and then the rest is ordered as i wanted. So when i refresh the page i can see these older random posts in the UI at the top of the thread/feed of posts and when component is fully mounted it renders posts in the correct order. Not good.
I wanted to avoid sorting the array of posts on the frontend for performance concerns, am i wrong?
Redux initial state:
const initialState = {
posts: [],
userPosts: [],
currentPost: {
title: "",
text: "",
imgUrl: "",
},
scrapedPost: {},
comments: [],
replies: [],
likes: [],
error: "",
lastPostAdded: null,
lastReplyAdded: null,
lastDeleted: null,
sessionExpired: false,
users: [],
};
Redux root reducer:
import { combineReducers } from "redux";
import { postsReducer } from "./posts.reducer.js";
import { userReducer } from "./user.reducer.js";
export const rootReducer = combineReducers({
user: userReducer,
posts: postsReducer,
});
Redux store config:
import { applyMiddleware, createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistReducer, persistStore } from "redux-persist";
import autoMergeLevel2 from "redux-persist/lib/stateReconciler/autoMergeLevel2";
import storage from "redux-persist/lib/storage";
import thunk from "redux-thunk";
import { rootReducer } from "./reducers/root.reducer";
const composeEnhancer = composeWithDevTools({ trace: true, traceLimit: 25 });
const persistConfig = {
key: "root",
storage,
stateReconciler: autoMergeLevel2,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer, composeEnhancer(applyMiddleware(thunk)));
const persistor = persistStore(store);
export { store, persistor };
getPost action creator (using thunk middleware for async task):
export const getPosts = () => async (dispatch) => {
const accessToken = localStorage.getItem("jwt");
const request = {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${accessToken}`,
},
method: "get",
};
try {
const response = await fetch(API_POST, request);
const data = await response.json();
const { posts, likes, sessionExpired } = data;
if (sessionExpired) {
dispatch({ type: SESSION_EXPIRED, payload: sessionExpired });
return;
}
dispatch({ type: GET_POSTS, payload: { posts, likes } });
} catch (error) {
dispatch({ type: SET_ERROR_POST, payload: error.message });
}
}
the posts reducer:
export const postsReducer = (state = initialState, action) => {
switch (action.type) {
case GET_POSTS: {
const { posts, likes } = action.payload;
return { ...state, posts, likes };
}
case GET_LIKES: {
const { likes } = action.payload;
return { ...state, likes };
// all other actions...//
}
relevant part of the UI code (feed component):
const Feed = () => {
const [newUser, setNewUser] = useState(false);
const user = useSelector((state) => state.user);
const { isAuthenticated, isNewUser } = useSelector((state) => state.user);
const posts = useSelector((state) => state.posts.posts);
const dispatch = useDispatch();
const userLanguage = useLanguage();
useEffect(() => {
window.scrollTo(0, 0);
setNewUser(isNewUser);
return function cleanup() {
setNewUser(null);
};
}, [isNewUser]);
useEffect(() => {
dispatch(getPosts());
}, []);
return (
<Layout>
//some jsx...//
<button className="h-6 refreshBtn outline-none hover:cursor-pointer bg-blue-500
text-white rounded-full gap-1 flex items-center justify-center pl-2 pr-3 py-1
shadow transition-all duration-300 hover:bg-black hover:shadow-none group"
onClick={() => dispatch(getPosts())}
style={{ opacity: posts && posts.length !== 0 ? 1 : 0 }}>
<RefreshIcon className="h-4 w-4 pointer-events-auto transform transition
transform duration-500 group-hover:-rotate-180" />
<span className="text-xs pointer-events-auto capitalize">
{userLanguage?.feed.refreshBtn}</span>
</button>
<div className="posts-wrapper h-full w-full relative flex flex-col items-center
justify-center gap-4 pb-6">
{posts.length === 0
? (<Skeleton element="post" number={8} />)
: (posts.map((post) => <Post key={post.postId} post={post} />)}
</div>
</Layout>
};
posts ordered by Id on the backend: screenshot
posts in the redux store (as you can see by their postId, indexes 0 to 3 have nothing to do there) screenshot
so my questions:
- how come the array fetched is not in the same order in redux store?
- why does the UI flash the "wrong" order for a sec, then the correct order? how does it know the correct order if those 4 posts are still at the top in the store?
i'm confused here, any hint or help is appreciated! thanks
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
