0

I am posting a comment to an image, I want to be able to fetch the new comment state without having to refresh the page to see the new comment.

Ideally if this was something simple like this:

Pseudo Code

state = {
  items:[],
  text:''
}

onSubmit = e => {
 e.preventDefault();
 this.setState({
  items[...this.state.items, this.state.text]
 })
}

The text will show within the items array without having to refresh the code. However, I have something more complex.

Images will be fetched from the GET_IMAGES reducer, and then we use POST_COMMENT to post a comment.

Ideally, I am trying to append the newComment to the images array, and once again without having to refresh the page to see the comment.

data structure when posting a comment

{
  "id": 244,
  "image_title": "owlman",
  "img_url": "http://re********nncbp****iy.png",
  "created_at": "2019-06-16T18:41:08.890Z",
  "updated_at": "2019-06-16T18:41:08.890Z",
  "user_id": 1,
  "user": {
    "id": 1,
    "googleId": null,
    "username": "El*****",
    "password": "$2*****Ix/fNUhD40***F3GCjO62",
    "email": "e******om",
    "created_at": "2019-06-05T04:50:20.133Z",
    "updated_at": "2019-06-05T04:50:20.133Z"
  },
  "comments": [
    {
      "id": 243,
      "comment_body": "gooseeeeffsfs",
      "created_at": "2019-06-16T19:31:50.129Z",
      "updated_at": "2019-06-16T19:31:50.129Z",
      "user_id": 1,
      "image_id": 244
    },
  ]
}

reducer

import { GET_IMAGES, POST_COMMENT, DELETE_IMAGE, UPLOAD_IMAGE } from '../actions/types';

const initialState = {
    images:[],

}

export default  (state = initialState, action) => {
    switch (action.type) {
        case GET_IMAGES:
            console.log(action.data);
            return{
                ...state,
                images:action.data
            }

        case DELETE_IMAGE:
            return{
                ...state,
                images: state.images.filter( (img) => img.id !== action.payload)
            }

        case POST_COMMENT:

            const commentnewState = {...state}
            const myComments = commentnewState.images // old images

            const newComments = action.data
            console.log(myComments[0].user.username)
            // tyring to pass new commment, within the images array. unsuccessful as of now.
            return{
                images:[
                    myComments[0],
                    {
                        user:{
                            username:myComments[0].user.username
                        },
                        comments:{
                          comment_body: newComments.commentBody
                        },

                    },

                ]

            }
        default:
            return state;
    }
}

actions

// get images

 export const getImages = () => {
   return (dispatch) => {
     return Axios.get('/images/uploads').then( (response) => {
            const data = response.data;
            dispatch({ 
               type: GET_IMAGES,
               data
             })
        });
    }
}
// post comment

export const postComment = data => {
    return async (dispatch) => {
        return Axios.post('/images/newComment', data).then( (response )=> {
            const newComment = response.data;
            console.log(newComment);
            dispatch({type:POST_COMMENT, data})
        })
    }
}

The comments are being mapped as followed:

commentSubmit = (event, id) => {
   event.preventDefault();
   console.log(this.state.comment_body); // doesn't get console.log
   // note that commentBody is being used for the req.body as well so its called by req.body.commentBody
   const commentBody = this.state.comment_body
   const data = {   
       commentBody,
       id
   }   
   this.props.postComment(data);
   this.setState({
        comment_body: ''
   })

  }
    render(){
       const { img, deleteImg } = this.props

{img.comments.length > 0 ? <Typography style={{ padding:'30px 10px'}}  variant="h6" align="left">Commments </Typography> : null }
{img.comments.length > 0 ? (
    img.comments.map( (comment, i) => (
        <div key={i}>  
            <List>
                <ListItem alignItems="center"> 
                    <Typography color="primary" variant="body1">
                        {comment.comment_body}
                        </Typography>  
                </ListItem>
                <Typography style={{ margin:'0px 15px'}} variant="caption" align="right">{moment(comment.created_at).calendar()}</Typography> 
                <Divider variant="fullWidth" component="li" />
            </List>
        </div>       
    ))
):(
    <div>
        <Typography style={{  padding:'30px 10px'}}>No Commments Yet</Typography>
    </div>
)}
5
  • 1
    Try to add only relevant codes in your question, posting so much code made people avoid reading your whole question. Commented Jun 16, 2019 at 20:12
  • @randal, what is action.data? Is that the image being returned or the comment? You should be returning the image from your backend API. Commented Jun 16, 2019 at 20:14
  • just the comment. the comment is nested in the image. Commented Jun 16, 2019 at 20:24
  • ill console.log(action.data) Commented Jun 16, 2019 at 20:25
  • console.log(newComments) is { "commentBody": "dsdsd", "id": 244 } Commented Jun 16, 2019 at 20:26

1 Answer 1

1

Configure your backend post-route at Axios.post('/images/newComment', data) so that it responds with the updated Image, not the new comment. With an updated image, we have a corresponding id to use with an image, when we want to update the Images Reducer.

Then in your reducer, we can use .map() to create a new array and update the right image with the new comment.

case POST_COMMENT:
    return {
        ...state,
        images: state.images.map((image) => {
            if(image.id == action.data.id){ //assumes action.data.id now is an image id
                return {
                    ...image,
                    comments: [
                     ...image.comments,
                     {
                      comment_body: action.data.commentBody
                     }
                   ]
                }
            } else {
                return image
            }
        })
    }
Sign up to request clarification or add additional context in comments.

3 Comments

ok will try this out, just so you know the new comment is nested in the image. The image is already uploaded, its just a commented added to the image.
got it working, going to edit you're answer. the backend is fine. If you can will you elaborate more on why your solution works and why my attempt didn't work. thanks.
@randal, awesome! Thanks for the edit. This works because of two main reasons. 1) We are avoiding state-mutation by using a .map() to create a brand-new array. In your solution, the` myComments` variable is not a new copy of state, instead it points to the same reference. You're making mutations to it with comment_body: newComments.commentBody which goes against redux-principle. Reducer will not update correctly. 2) We are able to pin-point which specific image to update, instead of updating the entire array. In .map() when we find a match, we just update that image with new comment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.