1

How to update the state of the props when a user likes a post?

The props would need to automatically update when a user clicks like.

Currently, a user can like a post, and only on page refresh I am able to see the updated number of likes, which shows on

{this.props.likeCount}

What Component lifecycle would be best for seeing the updated props without refreshing the page? this application is using redux.

Like.js

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} from '../actions/';




class Like extends Component{

    constructor(props){
        super(props);

        this.state = {
            likes: null
        }
    }

    getLikes = (id) =>  {
        // console.log(id);
        this.props.getLikeCount(id)
        console.log(this.props.likeCount)
    }




    render(){
        return(
            <div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >

            <i style={{ marginRight: '140px'}} className="fa fa-heart-o">
                    <span style={{ marginLeft: '6px'}}>
                        <a href="#" onClick={this.props.like}>Like </a>

                        {this.getLikes(this.props.postId)}

                    </span>
                    {/* gets the like counts */}
                    {this.props.likeCount}
                </i>
            </div>
        )
    }
}



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


const mapDispatchToProps = (dispatch) => ({
    // pass creds which can be called anything, but I just call it credentials but it should be called something more 
    // specific.

    getLikeCount: (id) => dispatch(getLikeCount(id)),

    // Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like); 

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);
                 dispatch({type: GET_LIKES_COUNT, data})
             })

    }
}

Reducer

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

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

export default (state = initialState, action) => {
    switch (action.type) {

        case GET_LIKES_COUNT:
            // console.log(action.data)
            return({
                ...state,
                likes:action.data
            })



        default:
            return state
    }
}

edit(im getting a wierd infinite post loop)

wierd error

Mr Patel
  • 87
  • 2
  • 11
  • for more information about redux, see this page: https://redux.js.org/basics/example – nima Apr 21 '19 at 18:24

2 Answers2

3

Update the code to the following code.

GET_LIKES_COUNT handles the api action, of getting the number of likes for a post.

Without it, it will be always set to 0 likes on render.

ADD_LIKE action gives it the functionality of updating the state without refreshing the page.(i know that their is more specific term they call this in react, maybe its re-rendering) Update the state without re-rendering the component as well as the most important part which is making the api call to the backend to allow the user to like a post. We set likes to 0 to make it possible to upvote the state and it to have it updated without refresh.

Thanks for the assistance @novonimo.

Reducer

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

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

export default (state = initialState, action) => {
    switch (action.type) {
        // get number of likes from api
        case GET_LIKES_COUNT:
            // console.log(action.data)
            return({
                ...state,
                likes:action.data
            })

        case ADD_LIKE:
            return({
                ...state,
                likes: state.likes + 1
            })

        default:
            return state
    }
}

Actions

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

    }
}

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

    }
}

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); 
        }
    }
    clickLike =  (id) => () => {
        this.props.postLike(id);
    }
    render(){
        const {title, id, userId, removePost, createdAt, post_content, username, editForm, isEditing, editChange, myTitle, postUpdate, likes} = 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={this.clickLike(id)} postId={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);

Like.js

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} from '../actions/';
class Like extends Component{
    constructor(props){
        super(props);
        this.state = {
            likes: null
        }
    }
    getLikes = (id) =>  {
        // console.log(id);
        this.props.getLikeCount(id)
        console.log(this.props.likeCount)
    }
    render(){
        return(
            <div style={{float:'right', fontSize: '1.5em', color:'tomato'}} >
            <i style={{ marginRight: '140px'}} className="fa fa-heart-o">
                    <span style={{ marginLeft: '6px'}}>
                        <a href="#" onClick={this.props.like}>Like </a>
                        {this.getLikes(this.props.postId)}
                    </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)),
    // Pass id to the DeletePost functions.
});
export default connect(mapStateToProps, mapDispatchToProps)(Like);
Mr Patel
  • 87
  • 2
  • 11
0

React philosophy is based on remove Refresh pages on changes. so forget refresh in all react app.

in the component you can change code like this:

handleAddUpVote = ()=> this.props.dispatch(addUpVote()) 

return(
    <div onClick={this.handleAddUpVote}> sth </div>
)

and in action:

const ADD_UP_VOTE = "ADD_UP_VOTE";

const addUpVote = ({type: ADD_UP_VOTE});

export {ADD_UP_VOTE, addUpVote}

and finally, change your reducer:

initialState={
    voteCounter: 0
}

const Reducer = (state=initialState, action) => {
    switch(action.type){
      case(ADD_UP_VOTE):
        return{
          ...state,
          voteCounter: state.voteCounter + 1
    };
  }
}
nima
  • 7,796
  • 12
  • 36
  • 53
  • 1
    this is sorta confusing, but i will use this logic in my solution thx – Mr Patel Apr 21 '19 at 19:07
  • watch some video about redux implementation in youtube and other resources to be master in redux @MrPatel – nima Apr 21 '19 at 19:08
  • master in redux lol, i got it working ill post answer in a second. – Mr Patel Apr 21 '19 at 19:37
  • I made a new post on my app, and its acting wierd now. It updates the number of likes for all posts. Does my code look wierd to u, or off ? its like its doing some infinite posting on my backend. i think it has something to do with `()=> ()` – Mr Patel Apr 21 '19 at 19:59
  • @MrPatel I can guess what is your mistake, my post was an example of using redux in your app, but you must consider your data. for example, your data structure looks like this: posts: [{id:1, text: hello, likes:0},{id:2, text: world, likes: 1}] so you must dispatch propper post id to your reducer function, and reducer must use the map for find and update post with id 2. if this not enough, tell me to post this comment with more code example. – nima Apr 21 '19 at 20:08
  • @MrPatel I'm waiting. – nima Apr 21 '19 at 20:23
  • https://stackoverflow.com/questions/55786557/infinite-post-loop-when-calling-action – Mr Patel Apr 21 '19 at 20:38