Redux News Reader Uncaught TypeError: Cannot read properties of undefined (reading 'type')

Hi everyone, I’m working on the Redux News Reader project and I’m stuck at task 15 of the front-end path.
When I pass in a new comment and hit the submit button I got an error in my browser console saying:
Uncaught TypeError: Cannot read properties of undefined (reading ‘type’)
Could someone help me with solving this error?

I copied all the files and I’m working in VS code. (had a lot of trouble working on Codecademy with this project)
This is the link of the project at Codecademy: https://www.codecademy.com/paths/front-end-engineer-career-path/tracks/fecp-redux/modules/redux-middleware-and-thunks/projects/redux-news-reader

this is my code from the commentsSlice.js

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

export const loadCommentsForArticleId = createAsyncThunk(
  'comments/loadCommentsForArticleId',
  async (id) => {
    const response = await fetch(`api/articles/${id}/comments`);
    const json = await response.json();
    return json;
  }
);

export const postCommentForArticleId = createAsyncThunk(
  'comments/postCommentForArticleId',
  async ({ articleId, comment }) => {
    const requestBody = JSON.stringify(comment);
    const response = await fetch(`api/articles/${articleId}/comments`, {
      method: 'POST',
      body: requestBody,
    });
    const json = await response.json();
    return json;
  }
);

// Create postCommentForArticleId here.

export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    byArticleId: {},
    isLoadingComments: false,
    failedToLoadComments: false,
    createCommentIsPending: false,
    failedToCreateComment: false,
  },
  extraReducers: (builder) => {
    builder
      .addCase(loadCommentsForArticleId.pending, (state) => {
        state.isLoadingComments = true;
        state.failedToLoadComments = false;
      })
      .addCase(loadCommentsForArticleId.rejected, (state) => {
        state.isLoadingComments = false;
        state.failedToLoadComments = true;
      })
      .addCase(loadCommentsForArticleId.fulfilled, (state, action) => {
        state.isLoadingComments = false;
        state.byArticleId[action.payload.articleId] = action.payload.comments;
      })
      .addCase(postCommentForArticleId.pending, (state) => {
        state.createCommentIsPending = true;
        state.failedToCreateComment = false;
      })
      .addCase(postCommentForArticleId.rejected, (state) => {
        state.createCommentIsPending = false;
        state.failedToCreateComment = true;
      })
      .addCase(postCommentForArticleId.fulfilled, (state, action) => {
        state.createCommentIsPending = false;
        state.byArticleId[action.payload.articleId].push(
          action.payload.comment
        );
      });
  },
});

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;

this is my code from the Commentform.js

import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createCommentIsPending } from '../features/comments/commentsSlice';
import postCommentForArticleId from '../features/comments/commentsSlice';

export default function CommentForm({ articleId }) {
  const dispatch = useDispatch();
  const [comment, setComment] = useState('');

  // Declare isCreatePending here.

  const handleSubmit = (e) => {
    e.preventDefault();
    // dispatch your asynchronous action here!
    dispatch(postCommentForArticleId({ articleId, comment }));
    setComment('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <label for="comment" className="label">
        Add Comment:
      </label>
      <div id="input-container">
        <input
          id="comment"
          value={comment}
          onChange={(e) => setComment(e.currentTarget.value)}
          type="text"
        />
        <button className="comment-button">Submit</button>
      </div>
    </form>
  );
}

I’m having a similar issue, but I’m getting a "Cannot read properties of undefined (reading ‘id’). I think the issue may be with because the Comment component needs and id from the comment prop, and we don’t add that with the postCommentsForArticleId thunk creator. I think there may be an issue with the fake api the project uses add the id and text properties to the action payload, if that makes sense.

Hello again! I finally figured it out (at least for me.)
The fulfilled case function for postCommentForArticleId should push action.payload, noT action.payload.comment

.addCase(postCommentForArticleId.fulfilled, (state, action) => {

    state.createCommentIsPending = false;

    state.failedToCreateComment = false;

    state.byArticleId[action.payload.articleId].push(

      action.payload

Hi davidmason2050074510, Thank you for your advise. I’ve tried it and sad enough it didn’t work for me. I have tried several things and got also different errors back that I can’t solve. maybe I will try to complete this project later in the course.

This one was pretty frustrating! I’m not able to get the Redux or React Dev Tools to work when completing the exercises in Codecademy since the preview is in an iframe, which really makes things difficult to troubleshoot!

@davidmason2050074510 was right - the Comment component is expecting an id that isn’t being set on new comments. The action.payload containing the new comment that is being pushed onto state.byArticleId[action.payload.articleId] doesn’t have this id.

The Id is being used as a key prop, so needs to be unique. For the sake of getting through this exercise, I’m just using Math.random().toString() to produce a random string to use for the key:

.addCase(postCommentForArticleId.fulfilled, (state, action) => {
        state.createCommentIsPending = false;
        state.byArticleId[action.payload.articleId].push({...action.payload, id: Math.random().toString() });

Not a robust solution - it’d most likely be problematic if we were expanding this wee application to delete comments, but enough to get past this frustrating blocker!

Hope this helps!

Hi campbellandy, thank you for your reply. Both your and davidmason’s advice helped a lot. There was also another problem and I thought it was a bit weird. when I used the useSelectors in my script they didn’t run with the already imported variables from the commentsSlice.js file. Instead of using the variables I made callback functions who gained the info from the state directly. like so: const article = useSelector((state) => state.currentArticle.article) After writing my useSelectors in this manner and using your advice I finally got through the Redux News Reader project.

I’m glad that this helped, beta!

That’s strange about your current article selector - are you sure you weren’t invoking selectCurrentArticle when passing to useSelector()? I’ve definitely made that mistake a few times myself!

Here’s the implementation of my selector for current article, which seems to work:

In currentArticleSlice.js:

export const selectCurrentArticle = (state) => state.currentArticle.article;

and in the CurrentArticle component:

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  selectCurrentArticle,
  isLoadingCurrentArticle,
} from '../currentArticle/currentArticleSlice';
import FullArticle from '../../components/FullArticle';

const CurrentArticle = () => {
  const dispatch = useDispatch();
  const article = useSelector(selectCurrentArticle);
...

At least you found a way to work around the issue! For any future projects/challenges, I’m definitely going to work on them locally rather than on Codecademy - much easier to troubleshoot.

Have a good weekend!

I thought lets give it a try, maybe I switched two values in my comments.js file. Probably I did switch them indeed. I carefully set the new values for my article, comments and commentsAreLoading variables and everything runs still smoothly. It would be nice if Codecademy would give more opportunities to work locally on this kind of projects.
thank you for given me this hint and have a good weekend too.

.addCase(postCommentForArticleId.fulfilled,(state,action)=>{ state.createCommentIsPending = false; state.failedToCreateComment = false; state.byArticleId[action.payload.articleId].push(action.payload); return ( <ul className='comments-list'> {comments.map((comm,index) => <Comment key={index} comment={comm}/> )} </ul> );

To solve the same problem.

1 Like