Redux News Reader Project || Comments not loading

Hello Everyone,

I’m working on the Redux News Reader, and my code isn’t working quite properly. For one thing it says loading comment in the sample browser, top of the page even when I have not clicked an article, and it doesn’t show the comments related to any of the articles when one is clicked.

I suspect I made an error either on step 7(3) and/or on step 8(2)

Here’s a link to the project: Redux News reader

I’ve only edited commentsSlice.js and comments.js so far.

My code is as follows:

commentsSlice.js:

// Import createAsyncThunk and createSlice here. import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' // Create loadCommentsForArticleId here. export const loadCommentsForArticleId = createAsyncThunk( 'comments/loadCommentsForArticleId', async (articleId) => { const response = await fetch(`api/articles/${articledId}/comments`); const json = await response.json(); return json; } ) // Create postCommentForArticleId here. export const commentsSlice = createSlice({ name: 'comments', initialState: { // Add initial state properties here. byArticleId: { }, isLoadingComments: false, failedToLoadComments: false, }, // Add extraReducers here. extraReducers: { [loadCommentsForArticleId.pending]: (state, action) => { state.isLoadingComments = true; state.failedtoLoadComments = false; }, [loadCommentsForArticleId.fulfilled]: (state, action) => { state.byArticleId = action.payload.articleId; state.isLoadingComments = false; state.failedtoLoadComments = false; }, [loadCommentsForArticleId.rejected]: (state, action) => { state.isLoadingComments = false; state.failedtoLoadComments = true; } } }); export const selectComments = (state) => state.comments.byArticleId; export const isLoadingComments = (state) => state.comments.isLoadingComments; export const createCommentIsPending = (state) => state.comments.createCommentIsPending; export default commentsSlice.reducer;

and comments.js:

import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { loadCommentsForArticleId, selectComments, isLoadingComments, createCommentIsPending } from '../comments/commentsSlice'; import { selectCurrentArticle } from '../currentArticle/currentArticleSlice'; import CommentList from '../../components/CommentList'; import CommentForm from '../../components/CommentForm'; const Comments = () => { const dispatch = useDispatch(); const article = useSelector(selectCurrentArticle); // Declare additional selected data here. const comments = selectComments; const commentsAreLoading = isLoadingComments; // Dispatch loadCommentsForArticleId with useEffect here. useEffect(() => { if (article !== undefined) { dispatch(loadCommentsForArticleId(article.id)); } }, [dispatch, article]); const commentsForArticleId = article === undefined ? [] : comments[article.id] if (commentsAreLoading) return <div>Loading Comments</div>; if (!article) return null; return ( <div className='comments-container'> <h3 className='comments-title'>Comments</h3> <CommentList comments={commentsForArticleId} /> <CommentForm articleId={article.id} /> </div> ); }; export default Comments;

Thanks for your help in advance

This project has be totally stuck too. I had a similar error to you though that I was able to fix.

const comments = selectComments;
const commentsAreLoading = isLoadingComments;

I am fairly certain these are both selectors and it should be written like this:

 const comments = useSelector(selectComments);
 const commentsAreLoading = useSelector(isLoadingComments);
1 Like

Hi Both,

I have struggled with this quite a bit (well, for roughly 5 hours to be honest, out of which 4.5 have been debugging :slightly_smiling_face: ) but have managed to complete it - let me know if you’re still having challenges!

Cheers!
Gabriel

Hi there!

I’m stuck at Redux News Reader step 10. When I click an article, the article’s comments are not displayed – only a blank page. The console says: Uncaught TypeError: “commentsForArticleId” is read-only

Maybe you can help me out if you have resolved the project…

commentsSlice.js

// Import createAsyncThunk and createSlice here.
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
// Create loadCommentsForArticleId here.
export const loadCommentsForArticleId = createAsyncThunk('comments/loadCommentsForArticleId', async (id) => {
  const response = await fetch(`api/articles/${id}/comments`);
  // .json() is also asynchronous
  const json = await response.json();
  return json;
});

// Create postCommentForArticleId here.

export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    // Add initial state properties here.
    byArticleId: {

    },
    isLoadingComments: false,
    failedToLoadComments: false
  },
  // Add extraReducers here.
  extraReducers: {
    [loadCommentsForArticleId.pending]: (state, action) => {
      state.isLoadingComments = true;
      state.failedToLoadComments = false;
    },
    [loadCommentsForArticleId.rejected]: (state, action) => {
      state.isLoadingComments = false;
      state.failedToLoadComments = true;      
    },
    [loadCommentsForArticleId.fulfilled]: (state, action) => {
      state.isLoadingComments = false;
      state.failedToLoadComments = false;
      // add the fetched comments
      state.byArticleId = action.payload.articleId;
    }
  }
});

export const selectComments = (state) => state.comments.byArticleId;
export const isLoadingComments = (state) => state.comments.isLoadingComments;
export const createCommentIsPending = (state) => state.comments.createCommentIsPending;

export default commentsSlice.reducer;

Comments.js

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  loadCommentsForArticleId,
  selectComments,
  isLoadingComments,
} from '../comments/commentsSlice';
import { selectCurrentArticle } from '../currentArticle/currentArticleSlice';
import CommentList from '../../components/CommentList';
import CommentForm from '../../components/CommentForm';

const Comments = () => {
  const dispatch = useDispatch();
  const article = useSelector(selectCurrentArticle);
  // Declare additional selected data here.
  const comments = useSelector(selectComments);
  const commentsAreLoading = useSelector(isLoadingComments);

  // Dispatch loadCommentsForArticleId with useEffect here.
  useEffect(() => {
    if (article !== undefined) {
      dispatch(loadCommentsForArticleId(article.id))
    } 
  }, [dispatch, article]);

  const commentsForArticleId = [];
  if (article !== undefined) {
    commentsForArticleId = comments[article.id];
  }

  if (commentsAreLoading) return <div>Loading Comments</div>;
  if (!article) return null;

  return (
    <div className='comments-container'>
      <h3 className='comments-title'>Comments</h3>
      <CommentList comments={commentsForArticleId} />
      <CommentForm articleId={article.id} />
    </div>
  );
};

export default Comments;

commentList.js

import React from 'react';
import Comment from './Comment';

export default function CommentList({ comments }) {
  if (!comments) {
    return null;
  }
  
  return (
    <ul className='comments-list'>
      {
        comments.map(x => {
          <Comment comment={x} />
        })
      }
    </ul>
  );
}

I think I got a bit further by changing

  const commentsForArticleId = [];
  if (article !== undefined) {
    commentsForArticleId = comments[article.id];
  }

to this

 const commentsForArticleId = [];
  if (article !== undefined) {
    commentsForArticleId.push(comments[article.id]);
  }

I mean that if I click an article, as an example LG says it might quit the smartphone market, the page loads and I can see the comment in console:

{id: 2, articleId: 2, text: 'Sad, I love my LG phone :('}

Nevertheless, the comment is not displayed on the page.

OK I found a few issues and got it working.

Needed to change this

 const commentsForArticleId = [];
  if (article !== undefined) {
    commentsForArticleId.push(comments[article.id]);
  }

…to this:

const commentsForArticleId = article ? comments[article.id] : [];

Also had to add a return statement for the Comment component:

    <ul className='comments-list'>
      {
        comments.map(txt => {
          return <Comment comment={txt} />
        })
      }
    </ul>
2 Likes

Hey guys,

I’m really struggling with the correct statement for postCommentsForArticleId.fulfilled section in the extraReducers object. Please help me to review my code, this is my current code that does not work when I submit a new comment:

[postCommentsForArticleId.fulfilled]: (state, action) => {
state.byArticleId.push[action.payload.articleId].push(comment => {
return action.payload.comment
});
state.createCommentIsPending = false;
state.failedToCreateComment = false;
}

Any feedback would be really helpful thanks!

This…

state.byArticleId.push[action.payload.articleId].push(comment => {
return action.payload.comment
});

needs to be written like this…

state.byArticleId = action.payload;

That being said, I also have not been able to get any comments to load or post…

I tried your solution and got it working! (well, somehow, since what gets rendered is the comment form, but not the comments themselves… ).

Anyway, maybe you can help me figuring out why that ternary operator works and the following not:

const commentsForArticleId = (article) => {
    if (article) {
      return comments[article.id];
    } else{
    return [];
}
  }

Or this:

 const commentsForArticleId = (article) => {
    if (article === undefined) {
    return [];
    }
    return comments[article.id];
  } 

Replying to myself: ternary operator retunrs a value right after declared. If... else function statements msut be executed after declaration. So in order for Comments component to pass commentsForArticleId correctly to CommentsList, it has to be passed as follows:

<CommentList comments={commentsForArticleId()} />

This will work the same as using a ternary operator for commentsForArticleId and passing it as a prop as follows:

<CommentList comments={commentsForArticleId} />

@tomofromearth I was having the same struggles, but I solved it as follows:

If you look back into step 4, a recommended format for storing the comments is suggested:

{
  123: ['Great article!' , 'I disagree.']
  456: ['This is some great writing.'],
  ... 
}

No further instructions on how to accomplish this are provided, so you may have skipped it (as I did), assuming that the object returned would already be in the format proposed, which is not the case.

In order to format the action.payload in that way, we need to look into how the payload actually looks like:

{
    "articleId": 5,
    "comments": [
        {
            "id": 1,
            "articleId": 5,
            "text": "Congratulations Kamala."
        },
        {
            "id": 2,
            "articleId": 5,
            "text": "Wow, very cool."
        }
    ]
}

The way I got it stored in byArticleId formated in the right way, so later on It can be used in Comments by commentsForArticleId using comments[article.id] looks like this:


      .addCase(loadCommentsForArticleId.fulfilled, (state, action) => {
        state.isLoadingComments = false;
        state.failedToLoadComments = false;
        state.byArticleId = {[action.payload.articleId]: action.payload.comments};
        
      })

Now, when selecting an article, my byArticleId property returns an object with the expected format and the comments will be rendered:

byArticleId: {
      '5': [
        {
          id: 1,
          articleId: 5,
          text: 'Congratulations Kamala.'
        },
        {
          id: 2,
          articleId: 5,
          text: 'Wow, very cool.'
        }
      ]
    }
2 Likes
Hello

I also encountered the same problem. The browser does not display anything after I click the button ‘Submit’ in the comment section. I checked with ChatGPT for bugs but it could not find any. I have refreshed the browser many times but the problem still persists. Here is my code and appreciate any feedback to fix this. Thank you.

Hi, did you figure this out? When I submit a comment, the comment doesn’t show under the article unless I click on the article to load the article again. Then it shows the comments I submitted. Is that what was happening with yours? Were you able to figure it out?

I figured out my problem. I didn’t destructor my comments using …rest. I left out the … when destructoring articleId and comment in the following:

const { articleId, …comment } = action.payload;
const existingComments = state.byArticleId[articleId] || ;
state.byArticleId = {
…state.byArticleId,
[articleId]: [ …existingComments, comment ],
};

edit: added a bracket ] that I left out