Search code examples
javascriptreactjsreduxredux-toolkit

How can I show or hide a div as I toggle an element from a list?


I have a list of elements where each element is a Game and as I toggle I dispatch an action to my slice where I want to affect the value of isDivOpen but just for the element (Game) I clicked on.

I created the following slice:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

const initialState = {
  games: [],
  isLoading: false,
  isDivOpen: true,
};

export const gameSlice = createSlice({
  name: 'games',
  initialState,
  reducers: {
    toggleSeeMore(state, action) {
      const gameId = action.payload;
      console.log(gameId);
    },
  },
})

export const { toggleSeeMore } = gameSlice.actions;

export default gameSlice.reducer;

And the following is my component:

import React from 'react';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import '../../styles/components/Games/Game.css';
import { useSelector, useDispatch  } from 'react-redux';
import { toggleSeeMore } from '../../slices/gameSlice';

function Game({ game }) {
  const { name, description, employees, hours, photo } = game;
  const isDivOpen = useSelector(state => state.games.isDivOpen);
  
  const dispatch = useDispatch()

  const toggleMenu = () => {
    const gameId = game._id
    dispatch(toggleSeeMore(gameId))
  }

  return (
    <div className='game_card'>
      <div className="game_heading">
        <div className="image_container">
          <img src={`http://localhost:5000/uploads/${photo}`} alt="no photo" />
        </div>
        <h1>{name}</h1>
      </div>
      <div className="game_content">
        <p className="description">{description}</p>
        <div className="game_hours">
          {formattedTimes.map((time, index) => (
            <div key={index}>{time}</div>
          ))}
        </div>
      </div>
      <button onClick={toggleMenu}>See More<ExpandMoreIcon/></button>
      <div className={`employees ${isDivOpen ? 'show' : 'hide'}`}>
        <p>Employees</p>
        {employees.map((employee, index) => (
          <div key={index}>{employee}</div>
        ))}
      </div>
    </div>
  );
}

export default Game;

I am receiving the correct id of the game I'm clicking on, but I don't know how to change the value of isDivOpen just for that game.


Solution

  • You are on the right track. Instead of using isDivOpen as a boolean value though, you should store the passed gameId or null when deselecting the game. The UI will check the current openGameId value against its own game id value, and set the appropriate conditional value.

    Example:

    const initialState = {
      games: [],
      isLoading: false,
      openGameId: null,
    };
    
    
    export const gameSlice = createSlice({
      name: 'games',
      initialState,
      reducers: {
        toggleSeeMore(state, action) {
          const gameId = action.payload;
    
          // Toggles openGameId to gameId if not equal to current state, 
          // otherwise openGameId is toggled back to null to deselect
          state.openGameId = state.openGameId === gameId ? null : gameId;
        },
      },
      
    })
    
    function Game({ game }) {
      const dispatch = useDispatch();
      const { openGameId } = useSelector(state => state.games);
    
      const { _id: gameId, name, description, employees, hours, photo } = game;
    
      // Compute if this game is open
      const gameIsOpen = openGameId === gameId;
    
      const toggleMenu = () => {
        dispatch(toggleSeeMore(gameId));
      }
    
      return (
        <div className='game_card'>
          ...
          
          <button onClick={toggleMenu}>
            See More<ExpandMoreIcon />
          </button>
          <div className={`employees ${gameIsOpen ? 'show' : 'hide'}`}>
            ...
          </div>
        </div>
      );
    }
    
    export default Game;