Redux News Reader step 9

I’m working on the Redux news reader task 9 (I think).

I believe my problem is step 9.2
( 1. Define a constant, commentsForArticleId, which should be an empty array when article is undefined and otherwise should be equal to comments[article.id].)
here:

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

But When I look at other peoples solutions that is exactly what they did.

comments contains the object it’s supposed to contain but commentsForArticleId remains undefined as does comments[article.id]

Here is the full code for Comments.js, as you can see I wrote some console.log in the middle and commented what they log

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. might be wrong
  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 = article ? comments[article.id] : [];

//tests
if (article !== undefined) {
  // prints comments: {"articleId":2,"comments":[{"id":2,"articleId":2,"text":"Sad, I love my LG phone :("}]}
  console.log('comments: ' + JSON.stringify(comments));
  // prints commentsForArticleId: undefined
  console.log('commentsForArticleId: ' + commentsForArticleId);
  //prints commentsForArticleId stringify: undefined
  console.log('commentsForArticleId stringify: ' + JSON.stringify(commentsForArticleId));
  // prints articleID: 2
  console.log('articleID: ' + article.id);
  //prints comments[article.id]: undefined
  console.log('comments[article.id]: ' + 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;

Here is my code for commentsSlice in case that’s useful

// Import createAsyncThunk and createSlice here.
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// Create loadCommentsForArticleId here.
export const loadCommentsForArticleId = createAsyncThunk(
  'comments/loadComments',
  async (id) => {
    const response = await fetch(`api/articles/${id}/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: builder => {
    builder
      .addCase(loadCommentsForArticleId.pending, (state) => {
        state.isLoadingComments = true;
        state.failedToLoadComments = false;
      })
      .addCase(loadCommentsForArticleId.fulfilled, (state, action) => {
        state.isLoadingComments = true;
        state.failedToLoadComments = false;
        state.byArticleId = action.payload;
      })
      .addCase(loadCommentsForArticleId.rejected, (state) => {
        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;

I’ve been stuck here for hours and while I’ve gotten closer to a solution I still can’t work it out so would really appreciate some help.

Apparently, your comments object does not have a number key. But that is what you are trying to get here:

comments[article.id]

article.id is a number, e. g. 2. So your comments object need to look like this:

{
    "1": [],
    "2": [...]
}

You get a comments object that does not seem to store the comments of all articles, but just those of one article. You could probably get that by doing:

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

But the problem seems to be in your extra reducers for loadCommentsForArticleId. You just drop the returned object into the property byArticleId:

That needs to be refactored so comments is an object structured as above.

Hint

Split the payload. Create the number keys on left hand side of the assignment and just assign the values of the comments property of the payload.

Thank you!
I changed it to

state.byArticleId = {[action.payload.articleId]: action.payload.comments};

Which I found in another thread on the forum. I’ve had a break from studying for a while so I’ve forgotten some things. I know it’s good real world training to find solutions by yourself but I really feel some of the exercises should have better guidance.

After fixing this problem the comments got stuck on “loading comments” It took me way too long to realise that my extra reducer had another problem, isLoadingComments was still true after loading :upside_down_face:

1 Like