0

I'm trying to tell react/redux to upvote a likeCount when ADD_LIKE is called from the reducer.

However its upvoting the number for all post items

How can i do something like.

if(action.id === post.id) then do likes: state.likes + 1

That way it can only upvote for that one post only.

case ADD_LIKE:
    // console.log(action.id)
    return({
        ...state,
        likes: state.likes + 1
    })

Actions.js

export const postLike = (id) => {
    return (dispatch) => {
        // console.log(userId);
        return Axios.post('/api/posts/like', {
            postId: id
        }).then( (like) => {
            dispatch({type: ADD_LIKE, id})
                // console.log('you have liked this', like)
        }).catch( (err)=> {
                console.log('there seem to be an error', err);
        })

Full Reducer

import { ADD_LIKE, GET_LIKES_COUNT} from '../actions/';

const initialState = {
    post: [],
    postError: null,
    posts:[],
    isEditing:false,
    isEditingId:null,
    likes:0,
    postId:null
}

export default (state = initialState, action) => {
    switch (action.type) {
        case GET_LIKES_COUNT:
            // console.log(action.data)
            return({
                ...state,
                likes:action.data
            })
         case ADD_LIKE:
            // console.log(action.id)
            return({
              ...state,
            likes: state.likes + 1
          })
        default:
            return state
    }
}

Like Component

import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCoffee, faAdjust } from '@fortawesome/free-solid-svg-icons';
import {connect} from 'react-redux';
import {  getLikeCount, postLike} from '../actions/';
class Like extends Component{
    constructor(props){
        super(props);
        this.state = {
            likes: null,
            heart: false
        }
    }
    // dont remove () after (id) or else infinite onClick
    clickLike = (id) => () =>  {
        this.props.postLike(id);
        // toggles between css class
        this.setState({
            heart: !this.state.heart
        })
    }
    render(){
       return(
            <div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
            <i style={{ marginRight: '140px'}} className={this.state.heart ? 'fa fa-heart':'fa fa-heart-o' }>
                    <span style={{ marginLeft: '6px'}}>
                        <a href="#" onClick={this.clickLike(this.props.like)}>Like </a>       
                    </span>
                    {/* gets the like counts */}
                    {this.props.likeCount}
                </i>
            </div>       
       )
    }
}
const mapStateToProps = (state) => ({
    isEditingId: state.post.isEditingId,
    likeCount:state.post.likes
})
const mapDispatchToProps = (dispatch) => ({
    getLikeCount: (id) => dispatch(getLikeCount(id)),
    postLike: (id) => dispatch( postLike(id))
    // Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like);

<Like like={id}/> post id is id and being passed in as a prop

Postitem.js

import React, { Component } from 'react';
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import moment from 'moment';
import Editable from './Editable';
import {connect} from 'react-redux';
import {UpdatePost, getLikeCount, postLike} from '../actions/';
import Like from './Like';
import Axios from '../Axios';
const Styles = {
    myPaper: {
        margin: '20px 0px',
        padding: '20px'
    },
    button:{
        marginRight:'30px'
    }
}
class PostItem extends Component{
    constructor(props){
        super(props);
        this.state = {
            disabled: false,
        }
    }
    onUpdate = (id, title) => () => {
        // we need the id so expres knows what post to update, and the title being that only editing the title. 
        if(this.props.myTitle !== null){
            const creds = {
                id, title
            }
            this.props.UpdatePost(creds); 
        }
    }
    render(){
        const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, likes,  clickLike} = this.props
        return(
            <div>
                   <Typography variant="h6" component="h3">
                   {/* if else teneray operator */}
                   {isEditing ? (
                          <Editable editField={myTitle ? myTitle : title} editChange={editChange}/>
                   ): (
                       <div>
                           {title}
                       </div>    
                   )}         
                   </Typography>
                   <Typography component="p">
                       {post_content}
                       <h5>
                           by: {username}</h5>
                       <Typography color="textSecondary">{moment(createdAt).calendar()}</Typography>
                       <Like like={id}/>
                   </Typography>
                   {!isEditing ? (
                       <Button variant="outlined" type="submit" onClick={editForm(id)}>
                           Edit
                       </Button>
                   ):(     
                       // pass id, and myTitle which as we remember myTitle is the new value when updating the title
                        <div>
                            <Button 
                                disabled={myTitle.length <= 3}
                                variant="outlined" 
                                onClick={this.onUpdate(id, myTitle)}>
                                Update
                            </Button>
                            <Button 
                                variant="outlined" 
                                style={{marginLeft: '0.7%'}}
                                onClick={editForm(null)}>
                                Close
                            </Button>
                        </div>
                   )}
                   {!isEditing && (
                    <Button
                        style={{marginLeft: '0.7%'}}
                        variant="outlined"
                        color="primary"
                        type="submit"
                        onClick={removePost(id)}>
                        Remove
                    </Button>
                    )}
           </div>
       )
    }
}
const mapStateToProps = (state) => ({
    isEditingId: state.post.isEditingId,
})
const mapDispatchToProps = (dispatch) => ({
    // pass creds which can be called anything, but i just call it credentials but it should be called something more 
    // specific.
    UpdatePost: (creds) => dispatch(UpdatePost(creds)),
    getLikeCount: (id) => dispatch(getLikeCount(id)),
    postLike: (id) => dispatch( postLike(id))
    // Pass id to the DeletePost functions.
});
export default connect(null, mapDispatchToProps)(PostItem);

PostList.js

import React, { Component } from 'react';
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import moment from 'moment';
import {connect} from 'react-redux';
import {DeletePost,  getLikeCount, postLike, UpdatePost,EditChange, DisableButton} from '../actions/';
import PostItem from './PostItem';
const Styles = {
    myPaper: {
        margin: '20px 0px',
        padding: '20px'
    }
}
class PostList extends Component{
    constructor(props){
        super(props);
        this.state ={
            title: '',
        }
    }
    // Return a new function. Otherwise the DeletePost action will be dispatch each
     // time the Component rerenders.
    removePost = (id) => () => {
        this.props.DeletePost(id);
    }
    onChange = (e) => {
        e.preventDefault();
        this.setState({
            title: e.target.value
        })
    }
    formEditing = (id) => ()=> {;
        this.props.EditChange(id);
    }
    getLikes = (id) =>  {
        // console.log(id);
        this.props.getLikeCount(id)
        console.log(this.props.likeCount)
    }
    render(){
        const {posts} = this.props;
        return (
            <div>
                {posts.map((post, i) => (
                    <Paper key={post.id} style={Styles.myPaper}>
                     {this.getLikes(post.id)}
                    {/* {...post} prevents us from writing all of the properties out */}
                        <PostItem
                             myTitle={this.state.title} 
                             editChange={this.onChange} 
                             editForm={this.formEditing} 
                             isEditing={this.props.isEditingId === post.id} 
                             removePost={this.removePost} 
                             {...post} 
                        />
                    </Paper>
                ))}
            </div>
        )
    }
}
const mapStateToProps = (state) => ({
    isEditingId: state.post.isEditingId,
})
const mapDispatchToProps = (dispatch) => ({
    // pass creds which can be called anything, but i just call it credentials but it should be called something more 
    // specific.
    EditChange: (id) => dispatch(EditChange(id)),
    UpdatePost: (creds) => dispatch(UpdatePost(creds)),
    getLikeCount: (id) => dispatch(getLikeCount(id)),
    postLike: (id) => dispatch( postLike(id)),
    // Pass id to the DeletePost functions.
    DeletePost: (id) => dispatch(DeletePost(id))
});
export default connect(mapStateToProps, mapDispatchToProps)(PostList);

Edit

Actions.js

export const getLikeCount = (id) => {
    return (dispatch, getState) => {
        return Axios.get(`/api/posts/likes/count/${id}`)
            .then( (res) => {
                 const data = res.data
                 console.log(data); // logs data and i can see an array 
                 dispatch({type: GET_LIKES_COUNT, data})
             })

    }
}

{this.props.likeCount} logs

enter image description here

enter image description here

ive tried doing this

case ADD_LIKE:
    return {
    ...state,
    posts: state.posts.map(post => {
      if (post.id === action.id) {
        return {
          ...post,
          likes: post.likes + 1
        }
      } else return post
    })
  };

but it does not update the like count for some reason.

4
  • You have only one counter for likes in state? If you want to have different numbers, then you need different states for that Commented Apr 22, 2019 at 12:17
  • did you mean post.id is postId in your state. because you can not compare if(action.id === post.id) then do likes: state.likes + 1 like this because post is array. Commented Apr 22, 2019 at 12:26
  • Maintaining likes in the state doesn't makes sense unless you are maintaining the number of posts liked by user which this redux state tracks. You'd need to update the posts array to increment the number of likes for that particular post. Commented Apr 22, 2019 at 12:54
  • @DeepakKhillare please check comments below sathish solution. Commented Apr 22, 2019 at 12:55

2 Answers 2

1

I'm not sure how your code works, but I assume Like component is like a love icon for each post? But from your code, seems like all the Like components are sharing the same redux state post.likes

const mapStateToProps = (state) => ({
    isEditingId: state.post.isEditingId,
    likeCount:state.post.likes
})

That is why all the Like share the same likes count. I will suggest to restructure your redux state, such as putting the likes count into the post object, for example:

Reducer

import { ADD_LIKE, GET_LIKES_COUNT} from '../actions/';

const initialState = {
    post: [],
    postError: null,
    posts:[], // put likes count in post item, for eg { id: number, likes: number }
    isEditing:false,
    isEditingId:null,
    postId:null
}

export default (state = initialState, action) => {
    switch (action.type) {
         case ADD_LIKE:
            return({
              ...state,
            posts: state.posts.map(post => (post.id === action.id ? { ...post, likes: post.likes + 1 } : post))
          })
        default:
            return state
    }
}

Then instead of use Redux in Like component, just passed the data from PostItem will be more easy to maintain:

PostItem

render() {
    return (
        ....
        <Like likeCount={post.likes} onClick={() => this.onClick(post.id)} />
        ....
    )
}
Sign up to request clarification or add additional context in comments.

9 Comments

im still trying to work this logic, but will accept as answer.
yea sure, let me know again if you face any problem :)
it didn't work really work. ill post the likeCount log, because i can actually get a list of values, but it needs to know where to put it for each post
Im just back at square one, with the original code. I apologize, because i actually missed a portion of the code that actually handled get the post like counts.
In an simple way, how would i be able to tell redux to filter through the array action.data, and set the postLikeCount for that specific post.id
|
0

try this, did you mean post.id instead of postId in your state. because you can not compare if(action.id === post.id) then do likes: state.likes + 1 like this because post is array.

if you wrongly used post.id instead of postId you can try below solution

export default (state = initialState, action) => {
    switch (action.type) {
        case GET_LIKES_COUNT:
            // console.log(action.data)
            return({
                ...state,
                likes:action.data
            })
         case ADD_LIKE:
            // console.log(action.id)
            return({
              ...state,
            likes: action.id === state.postId? state.likes + 1 : state.likes
          })
        default:
            return state
    }
}

4 Comments

this doesn't seem to work. i will post how im getting post.id. one sec.
action.id gets the post.id but i need a way to make state.likes upvote only the post containing the action.id.
if i do likes: action.id !== state.postId? state.likes + 1 : state.likes it will update state for all post still
That means the data type in action.id and state.postId is not same. You may want to use parseInt function to cast the value to integer.

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.