Redux News Reader - can't get comment text to show up on page from returned object

Really? That’s awesome…!

Comments.js

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]);
  
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;```


**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(comment => {
        return <Comment comment={comment} />
      })}
    </ul>
  );
}```

**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`);
    const json = await response.json();
    console.log(json);
    return json;
  }
)

// Create postCommentForArticleId here.

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

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;```

in Comments.js:

useEffect(() => {
  if (article !== undefined) {
    dispatch(loadCommentsForArticleId)};
  }, [article]);

loadCommentsForArticleId needs to be called with the article id. I might have confused you here. What I meant was you include the article, not the article.id, in the dependency array (as in, this gets called any time the article changes).

Should be:

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

Otherwise, that one should hopefully be fine.


in CommentsSlice…

.addCase(loadCommentsForArticleId.fulfilled, (state, action) => {
        state.isLoadingComments = false;
        state.byArticleId[article.id] = action.payload;
        state.failedToLoadComments = false;

I’m sorry, that one was my fault, I was writing on my phone while waiting for my daughter to come out of daycare so I was distracted. It should be
state.byArticleId[action.payload.articleId] = action.payload.comments (I’m not sure if you 100% need the comments on the end there, but have a go.

1 Like

@jonrosk089 can you confirm your fulfilled extraReducer for loadCommentsForArticleId?
Mine has a ‘.’ after the ‘state.byArticleId’:

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

(@martyg_london)

Yep, it’s correct as I wrote it. I’m sure your way also works.

1 Like

@jonrosk089

Don’t know what to say… It works! Not sure I would ever have got there on my own!

Crazy to see how excited I was to finally see a little comment pop in… :grinning:

Thanks so much for taking the time, have a great weekend.

Now… onto posting comments… task 10 here I come…

1 Like

Howdy,

I know I’m a few days behind, but I am having trouble getting any comments to show up too. I’ve read through this thread, and tried multiple methods, but for some odd reason I’m not able to get it to work. My main problem is that for some reason my chrome devtools don’t show the updated states, so I’m wondering if it’s more of a browser issue. Here is my breakdown for what I suspect to be the culprit:

Comments.js:

Comments.js
//import info block

const Comments = () => {
  const dispatch = useDispatch();
  const article = useSelector(selectCurrentArticle);
  // Declare additional selected data here.
  const comments = useSelector(selectComments);
  const commentsAreLoading = useSelector(isLoadingComments);
  const commentsForArticleId = article ? comments[article.id] : [];
  // Dispatch loadCommentsForArticleId with useEffect here.
  useEffect( ()=>{
    if(article){
      dispatch( loadCommentsForArticleId(article.id) );
    }
  },[dispatch,article])



  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;

commentsSlice.js:

commentsSlice.js
// import info block

// Create loadCommentsForArticleId here.
export const loadCommentsForArticleId = createAsyncThunk(
  'comments/loadCommentsForArticleId',
  async (id) => {
    const response = await fetch(
      `api/articles/${id}/comments`
    );
    const json = await response.json();
    console.log(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`,
      {
        method: 'POST',
        body: requestBody
      }
    );
    const json = await response.json();
    return json;
  }
)


export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    // Add initial state properties here.
    byArticleId:{},
    isLoadingComments: false,
    failedToLoadComments: false,
    createCommentIsPending: false,
    failedToCreateComment: false,
  },
  // Add extraReducers here.
  extraReducers:{
    [loadCommentsForArticleId.pending]: (state,action)=>{
      state.isLoadingComments= true;
      state.failedToLoadComments= false;
    },
    [loadCommentsForArticleId.fulfilled]: (state,action)=>{
      state.byArticleId[action.payload.articleId]= action.payload.comments;
      state.isLoadingComments= false;
      state.failedToLoadComments= false;
    },
    [loadCommentsForArticleId.rejected]: (state,action)=>{
      state.isLoadingComments= false;
      state.failedToLoadComments= true;
    },
   //postCommentArticleId extrareducers here...
});

And though I doubt this is the cause, here is what I have for Commentlist.js:

CommentList.js
import React, { useEffect } from 'react';
import Comment from './Comment';

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

Any help would be wonderful, I fully agree that this project is a brain-melter.

Edit: fixed typo in post and put code in detail blocks.

Hi @j-neff,
Does it definitely not work? What is really silly about this exercise is that not all the articles have comments. Try clicking on the Kamala Harris one.

I say that because I copied and pasted your relevant stuff into my working version and it still works. So, the problem isn’t with what you’ve posted, seemingly. One mini thought that I had is that you’re importing useEffect in CommentList.js, which you don’t need, and as a result I was wondering if maybe you imported it into the wrong file?

1 Like

Thanks for the response and checking @jonrosk089!

I’m thinking that it must be a browser/cache issue, because I closed out chrome, and re-logged and now it works. I can see all of the original comments and any I add (I proceeded to work on the other steps to see if that was my issue). I did remove that import of useEffect (thank you for catching that btw), but now the comments appear whether it’s there or not. Weird…

Great, glad it’s working! The importing it wouldn’t actually change anything, it just makes stuff a bit slower (not noticeably so in this case). When you do something like this in a text editor it’ll just notify

// 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.
// from 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',
        body: requestBody
      }
    );
    const json = await response.json();
    return json;
  }
);
//until here
export const commentsSlice = createSlice({
  name: 'comments',
  initialState: {
    // Add initial state properties here.
    byArticleId: {},
    isLoadingComments: false,
    failedToLoadComments: false,
    createCommentIsPending: false,
    failedToCreateComment: false
  },
  // Add extraReducers here.
  extraReducers: {
    [loadCommentsForArticleId.pending]: (state, action) => {
      state.isLoadingComments= true;
      state.failedToLoadComments= false;
    },
    [loadCommentsForArticleId.fulfilled]: (state, action) => {
      state.isLoadingComments= false;
      state.failedToLoadComments= false;
      state.byArticleId[action.payload.articleId]= action.payload.comments;
    },
    [loadCommentsForArticleId.rejected]: (state, action) => {
      state.isLoadingComments= false;
      state.failedToLoadComments= true;
    },
    //probably here
    [postCommentForArticleId.pending]: (state, action) => {
    state.createCommentIsPending = true;
    state.failedToCreateComment = false
    },
    [postCommentForArticleId.fulfilled]: (state, action) => {
    state.createCommentIsPending = false;
    state.failedToCreateComment = false;
    state.byArticleId[action.payload.articleId].push(action.payload);
    },
    [postCommentForArticleId.rejected]: (state, action) => {
    state.createCommentIsPending = false;
    state.failedToCreateComment = true;
    }
    //until here
  }
});

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;

Hi, everyone. It is supposed to add the comments to the page (step 11-14) . I’ve tried to re-check the spelling, unluckily, I found nothing, so I’m a bit lost. could anyone help? thanks in advance :slight_smile:

Hey there, I’m not seeing any difference between what you have there and my working version; could you post the other relevant code?

thank you for your reply @jonrosk089

Comment.js

Summary
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);
  const commentsForArticleId = (article === undefined) ? [] : comments[article.id]; 
  // Dispatch loadCommentsForArticleId with useEffect here.
  useEffect(() => {
    if (article !== undefined) {
      dispatch(loadCommentsForArticleId(article.id))};
  }, [dispatch, article]);

  

  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;

CommentForm.js

Summary
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 isCreatePending = useSelector(createCommentIsPending);

  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
          disabled={isCreatePending}
          className='comment-button'
        >
          Submit
        </button>
      </div>
    </form>
  );
}

CommentList.js

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

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

ahh my bad, I checked my terminal and I found missing ‘}’ thank you very much for your reply though :sweat_smile:

1 Like

Ah yeah, now you say it it’s super obvious, but I completely missed that.

Hi there,

It looks like i am stuck and i have spent a lot of time on this but seems not working. I am stuck on step 9 and really need this fixed but lost some hope.

I am constantly checking in console.log with all various output but not sure why this is not getting any comments.

All i can see is Loading Comments instead of showing any comments when i select any article. I have tried article 2 & 5 which has some comments but looks like nothing is printing.

i have added all my related files here for the review and if anyone can review my code and let me know that would be much appreciated.

1) 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`);

    const json = await response.json();

    console.log(json);

    return json;

  }

)

// Create postCommentForArticleId here.

export const commentsSlice = createSlice({

  name: 'comments',

  initialState: {

    // Add initial state properties here.

    byArticleId: {},

    isLoadingComments: false,

    failedToLoadComments: false

  },

  extraReducers: {

    [loadCommentsForArticleId.pending]: (state, action) => {

      state.isLoadingComments = true;

      state.failedToLoadComments = false;

    },

    [loadCommentsForArticleId.fulfilled]: (state, action) => {

      const {id} = action.payload;

      state.isLoadingCommentsc = false;

      state.failedToLoadComments = false;

      

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

    },

    [loadCommentsForArticleId.rejected]: (state, action) => {

      state.isLoadingComments = false;

      state.failedToLoadComments = true;

    }

  }

  // Add extraReducers here.

});

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;```

**2) 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);
  //let commentsForArticleId = [];
  // Dispatch loadCommentsForArticleId with useEffect here.

useEffect( () => {
 
  if(article !== undefined){
    dispatch(loadCommentsForArticleId(article.id));
    /*console.log(comments[article.id]);
    commentsForArticleId = comments[article.id];*/
  }
  /*else {
    commentsForArticleId = [];
  }*/
  
},[dispatch,article]);

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

const commentsForArticleId = (article === undefined) ? [] : comments[article.id];
//console.log(commentsForArticleId);

  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;```

**3) 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(comment => {
        return <Comment comment={comment} />
        })
      }
    </ul>
  );
} ```

Thanks in advance for taking your time and reviewing my code.

Hope to hear soon :slight_smile: 

Naiya

Hey there,
My immediate suggestion without checking it thoroughly is to check the spelling of everything in loadCommentsForArticleId.fulfilled - also, I’m not exactly sure why you’re trying to take the id from actionpayload, because you then don’t use it. That’s not a massive problem but it is inefficient. I guess maybe you meant something like this…

const { articleId, comments } = action.payload;

....

state.byArticleId[articleId] = comments
1 Like

Thanks @jonrosk089. You are a :star:

I spent so much time reading the same code again and again so literally could not see the typo.

I have corrected this and this is working now.

Once again thank you so much for your help. :slight_smile:

I would definitely would like to DM you and hope you don’t mind just in case if i need any further help in future.

To be honest, i might need help with Reddit project as it looks bit challenging where you need to write everything from scratch and i am bit confused with react redux concept still.

Hope you don’t mind if i need to ask you for the review or need any help.

Once again thank you for your time and help.

Hope you have a good day/night :slight_smile:

Naiya

Hey, of course, feel free to write! Typos happen - a lot of these little mistakes are much easier to spot/fix when you’re doing your own stuff in a text editor, but with these complicated projects, it can be very hard to see the wood for the trees. Glad you got it working!

1 Like

Thanks @jonrosk089

Have you tried Quote Api project on Node module as i wrote them but seems can’t see anything and keeps giving errors.

Please let me know once you done so i can share my code with you for the review. :slight_smile:
Naiya

Hey,
Oh, you’ve shot ahead there! I just did that one today. Feel free to post the code in a DM or something, or maybe start a new get help thread, it’s good if other people have the same problem. I won’t reply tonight (just heading to bed, tiny person will wake me up at 6) but will get back to you tomorrow morning!