Redux News Reader - postCommentForArticleId.fullfilled posts empty comments

I’m near the end of this challenge Redux News Reader and now stuck in the postCommentForArticleId.fulfilled reducer section. When I submit a new comment it’s supposed to add the new comment, but instead I get a blank entry (image below).

It may be related to how I’m either passing the arguments (e.g. in CommentForm.js, commentSlice.js, etc.). But I would like someone to take a look at my files to see if they know what I did wrong.

I left my gist here.

commentSlice.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`);
    const json = await response.json();
    return json;
  }
)
// Create postCommentForArticleId here.
export const postCommentForArticleId = createAsyncThunk(
  'comments/postCommentForArticleId',
  async ({articleId, comment}) => {
    const requestBody = JSON.stringify({comment: comment})
    const response = await fetch(`api/articles/${articleId}/comments`)
    const json = await response.json()
    return json
  }
)

export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    // Add initial state properties here.
    byArticleId: {
      // 123: ['Great article', 'I disagree.'],
    },
    isLoadingComments: false,
    failedToLoadComments: false,

    createCommentIsPending: false,
    failedToCreateComment: false,
  },
  // Add extraReducers here.
  extraReducers: (builder) => {
    // loadCommentsForArticleId
    builder 
      .addCase(loadCommentsForArticleId.pending, (state) => {
        state.isLoadingComments = true
        state.failedToLoadComments = false
      })
      .addCase(loadCommentsForArticleId.fulfilled, (state,action) => {
        state.isLoadingComments = false
        state.failedToLoadComments = false
        state.byArticleId[action.payload.articleId] = action.payload.comments
      })
      .addCase(loadCommentsForArticleId.rejected, (state) => {
        state.isLoadingComments = false
        state.failedToLoadComments = true
      })

    // postCommentForArticleId
    builder
      .addCase(postCommentForArticleId.pending, (state) => {
        state.createCommentIsPending = true
        state.failedToCreateComment = false
      })
      .addCase(postCommentForArticleId.fulfilled, (state, action) => {
        state.createCommentIsPending = false
        state.failedToCreateComment = false
        state.byArticleId[action.payload.articleId].push(action.payload)
      })
      .addCase(postCommentForArticleId.rejected, (state) => {
        state.createCommentIsPending = false
        state.failedToCreateComment = 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;

Comment.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`);
    const json = await response.json();
    return json;
  }
)
// Create postCommentForArticleId here.
export const postCommentForArticleId = createAsyncThunk(
  'comments/postCommentForArticleId',
  async ({articleId, comment}) => {
    const requestBody = JSON.stringify({comment: comment})
    const response = await fetch(`api/articles/${articleId}/comments`)
    const json = await response.json()
    return json
  }
)

export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    // Add initial state properties here.
    byArticleId: {
      // 123: ['Great article', 'I disagree.'],
    },
    isLoadingComments: false,
    failedToLoadComments: false,

    createCommentIsPending: false,
    failedToCreateComment: false,
  },
  // Add extraReducers here.
  extraReducers: (builder) => {
    // loadCommentsForArticleId
    builder 
      .addCase(loadCommentsForArticleId.pending, (state) => {
        state.isLoadingComments = true
        state.failedToLoadComments = false
      })
      .addCase(loadCommentsForArticleId.fulfilled, (state,action) => {
        state.isLoadingComments = false
        state.failedToLoadComments = false
        state.byArticleId[action.payload.articleId] = action.payload.comments
      })
      .addCase(loadCommentsForArticleId.rejected, (state) => {
        state.isLoadingComments = false
        state.failedToLoadComments = true
      })

    // postCommentForArticleId
    builder
      .addCase(postCommentForArticleId.pending, (state) => {
        state.createCommentIsPending = true
        state.failedToCreateComment = false
      })
      .addCase(postCommentForArticleId.fulfilled, (state, action) => {
        state.createCommentIsPending = false
        state.failedToCreateComment = false
        state.byArticleId[action.payload.articleId].push(action.payload)
      })
      .addCase(postCommentForArticleId.rejected, (state) => {
        state.createCommentIsPending = false
        state.failedToCreateComment = 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;

CommentList.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`);

    const json = await response.json();

    return json;

  }

)

// Create postCommentForArticleId here.

export const postCommentForArticleId = createAsyncThunk(

  'comments/postCommentForArticleId',

  async ({articleId, comment}) => {

    const requestBody = JSON.stringify({comment: comment})

    const response = await fetch(`api/articles/${articleId}/comments`)

    const json = await response.json()

    return json

  }

)

export const commentsSlice = createSlice({

  name: 'comments',

  initialState: {

    // Add initial state properties here.

    byArticleId: {

      // 123: ['Great article', 'I disagree.'],

    },

    isLoadingComments: false,

    failedToLoadComments: false,

    createCommentIsPending: false,

    failedToCreateComment: false,

  },

  // Add extraReducers here.

  extraReducers: (builder) => {

    // loadCommentsForArticleId

    builder 

      .addCase(loadCommentsForArticleId.pending, (state) => {

        state.isLoadingComments = true

        state.failedToLoadComments = false

      })

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

        state.isLoadingComments = false

        state.failedToLoadComments = false

        state.byArticleId[action.payload.articleId] = action.payload.comments

      })

      .addCase(loadCommentsForArticleId.rejected, (state) => {

        state.isLoadingComments = false

        state.failedToLoadComments = true

      })

    // postCommentForArticleId

    builder

      .addCase(postCommentForArticleId.pending, (state) => {

        state.createCommentIsPending = true

        state.failedToCreateComment = false

      })

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

        state.createCommentIsPending = false

        state.failedToCreateComment = false

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

      })

      .addCase(postCommentForArticleId.rejected, (state) => {

        state.createCommentIsPending = false

        state.failedToCreateComment = 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;

CommentForm.js

import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  createCommentIsPending,
  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!
    console.log('comment', comment)
    dispatch(postCommentForArticleId({articleId,comment}))
    setComment('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor='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>
  );
}

1 Like

My mistake. I found the problem:

I don’t have the second parameter for fetch.

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

Hello, I’m having bug as you even with this fix. Do you mind sharing your comment.js and commentList.js code? I noticed in your original post you had the commentSlice code in the sections your labelled for comment.js and commentList.js.

I was struggling too with a blank after the fixed and this is what I did:
commentsSlice.js - adding the postCommentForArticleId

// Create postCommentForArticleId here.
export const postCommentForArticleId = createAsyncThunk(
‘comments/postCommentForArticleId’,
async ( {articleId, comment}) => {
const requestBody = JSON.stringify(comment);
const options = {
method:‘POST’,
body: requestBody,
};
const response = await fetch(api/articles/${articleId}/comments, options);
const json = await response.json();
const responseBody = JSON.stringify(json);
const comments = {
id: json.id,
articleId: json.articleId,
text: requestBody
}
return comments;
}

Then, in the extraReducers for the postCommentForArticleId.fulfilled:
[postCommentForArticleId.fulfilled]: (state, action) => {
state.createCommentIsPending = false;
state.failedToCreateComment = false;
var object = {
id: action.payload.id,
text: action.payload.text.substring(1, action.payload.text.length-1),
articleId: action.payload.articleId
}
state.byArticleId[action.payload.articleId].push(object);

},