0

I created a like button and a dislike button. My image has the dislike button by default. When I click on the dislike button, I would like this button to disappear and the like button to take its place. My problem is that the state changes when I check it, but I don't see the change on the page except when I reload the page: the change is saved in the database. Does anyone have any idea how to solve this? Here is my reducer.

My initialState:

const initialState = {
    loading: false,
    stories: [],
    error: "",
    currentPage: 1,
    totalPages: null,
    totalStories: null
}

 case LIKE:
        return {
            ...state,
            stories: state.stories.map(storie => {
                if(storie.id === action.payload.id) {
                    return  { ...storie, 
                        userlikes: action.payload.userlikes
                    }

                } else {
                    return storie;
                }
            })
        }

This is all my reducer

import {FETCH_GALLERY_REQUEST, FETCH_GALLERY_SUCCESS, FETCH_GALLERY_FAILURE, GALLERY_CLEAR, LIKE, DISLIKE } from "./galleryTypes";

const initialState = {
    loading: false,
    stories: [],
    error: "",
    currentPage: 1,
    totalPages: null,
    totalStories: null
}

const galleryReducer = (state = initialState, action) => {
    switch(action.type) {
        case FETCH_GALLERY_REQUEST:
            return {
                ...state,
                loading: action.payload,
            }
            
        case GALLERY_CLEAR:
            return {
                ...state,
                loading: false,
                stories: [],
                error: "",
                currentPage: 1,
                totalPages: null,
                totalStories: null
        }
        
        case FETCH_GALLERY_SUCCESS:
            return {
                ...state,
                loading: false,
                stories: [...state.stories, ...action.payload].filter( 
                    (storie, index) => index === [...state.stories, ...action.payload].findIndex( 
                        elem => elem.id === storie.id && elem.id === storie.id
                        )
                    ),
                currentPage: action.currentPage,
                totalPages: action.totalPages,
                totalStories: action.totalStories,
                error: ""
            }
    
        case FETCH_GALLERY_FAILURE:
            return {
                ...state,
                loading: false,
                stories: [],
                error: action.payload
            }

        
        case LIKE:
            return {
                ...state,
                stories: state.stories.map(storie => {
                    if(storie.id === action.payload.id) {
                        return  { 
                            ...storie, 
                            userlikes: action.payload.userlikes
                        }

                    } else {
                        return storie;
                    }
                })
            }


        default: 
            return state;
    }
}

export default galleryReducer

this is my vue component

 // permet d'ajouter un like
addLike = (story_id, user_id, index) => {
    const data = {
        story_id: story_id,
        user_id: user_id
    }
    this.props.like(story_id);
    this.props.fetchLike(data);

}

// permet denlever un like
removeLike = (story_id, user_id) => {
    this.setState({
        story_id: story_id
    })

    const data = {
        story_id: story_id,
        user_id: user_id
    }

    this.props.disLike(story_id);
    this.props.fetchDislike(story_id, user_id);
    
}

my render

{storiesData.stories.map((storie, index) => (
  <div
    key={index}
    className="everyprofile-container_details item"
    style={{ height: "450px" }}
  >
    <div className="everyprofile_name">
      <span className="everyProfile-firstname"> {storie.firstname} </span>
      <span className="everyProfile-lastname"> {storie.lastname} </span>
    </div>
    <div className="everyprofile-photo_cadre">
      <div
        className="everyprofile-photo"
        style={{
          backgroundImage: `url(${config.API_URL}resources/pictures_min/${storie.picture_path})`,
          backgroundPositionY: `${storie.bg_position_y}%`,
          backgroundPositionX: `${storie.bg_position_x}%`,
          backgroundSize: "cover"
        }}
      >
        {storie.userlikes == 1 ? (
          <button
            onClick={() => this.removeLike(storie.id, user_id, index)}
            className="everyprofile-like-btn"
            style={{ color: "#FF0000" }}
          >
            <Icon size={40} icon={ic_favorite} />
          </button>
        ) : (
          <button
            onClick={() => this.addLike(storie.id, user_id, index)}
            className="everyprofile-like-btn"
            style={{ color: "#FF0000" }}
          >
            <Icon size={40} icon={ic_favorite_border} />
          </button>
        )}
      </div>
    </div>
    <div className="everyprofile-container-story">
      <div className="everyprofile-story">{storie.story}</div>
    </div>
  </div>
))}


const mapStateToProps = (state) => {
  return {
    storiesData: state.stories,
    loading: state.stories.loading,
    currentPage: state.stories.currentPage,
    totalPages: state.stories.totalPages,
    totalStories: state.stories.totalStories,
    validateStory: state.validateStorie.validateStory,
    searchStatus: state.searchStatus.searchBar,
    query: state.query.query
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    // fetchEveryProfile: (storyId, userId) => dispatch(fetchEveryProfile(storyId, userId)),
    fetchLike: (data) => dispatch(fetchLike(data)),
    fetchDislike: (story_id, user_id) => dispatch(fetchDislike(story_id, user_id)),
    like: (story_id) => dispatch(like(story_id)),
    disLike: (story_id) => dispatch(disLike(story_id)),


    fetchGalleryFirst: (searchInfo) => dispatch(fetchGalleryFirst(searchInfo)),
    fetchGallery: (searchInfo) => dispatch(fetchGallery(searchInfo))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(EvryProfile)

This is actions

// Permet d'enlever le bouton like
export const like = (story_id) => {
    return {
        type: LIKE,
        payload: {
            id: story_id,
            userlikes: 1
        }
    }

}

// Permet d'enlever le bouton dislike
export const disLike = (id) => {
    return {
        type: DISLIKE,
        payload: id
    }
}
3
  • 4
    Can you include a more Minimal, Complete, and Reproducible code example? The component with the buttons, action creator, full reducer, how the component connects to your redux store, etc...? In my experience this is usually caused by either mutating a state object or simply not refetching backend data (because you didn't optimistically update your local cache). Commented Jan 1, 2021 at 20:29
  • .map() on a list of objects is not enough to keep immutable state - the objects themselves in the list are still references - I suspect that may be your problem. See stackoverflow.com/questions/34716651/… Commented Jan 1, 2021 at 20:29
  • 2
    @ifo20 OP is shallow copying the array and the storie that is being updated, I don't see a mutation issue specifically in this snippet. Commented Jan 1, 2021 at 20:31

1 Answer 1

1

Issue

Your reducer appears to be missing the case handling DISLIKE. Based on your LIKE case and UI logic I would assume updating stories[someIndex].userlikes to any value other than 1 should toggle the dislike button back to a like button.

Tracing the steps

  1. Button is clicked and removeLike called
  2. removeLike dispatches disLike and fetchDislike actions
  3. I assume fetchDislike is an async action, i.e. thunk, epic, saga, etc... and works, but dislike should be handled in a reducer.
  4. Look in reducer and see no case handling DISLIKE

Solution

Add case to handle DISLIKE and update the specific story's userlikes property.

switch (true) {
  case DISLIKE:
    return {
      ...state,
      stories: state.stories.map((story) =>
        storie.id === action.payload.id
          ? {
              ...story,
              userlikes: 0
            }
          : story
      )
    };
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer, indeed I have an async action for the registration in database and another action for the change at the level of the view before reloading the page. When the button like or dislike is clicked, I see the change in React dev tools, but the view button does not change, unless we reload the page
This is what I do not understand. Do I make well a copy of my state or not
@ChrisIssifou All the reducer cases you shared all appear to correctly return a new state object, so I don't think that is the issue. I suspect it may some reducer case that updates the stories array and/or using the array index as the react key. I don't see where you allow add/remove/reorder from your UI. Could you create a running codesandbox example that reproduces this issue? Start with just the local code first (ignore the async actions for now).
Thank you so much, I found the problem, it comes from the puglin "owl carousel" that I use.

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.