Home > database >  Cannot change style with set state conditionally based on type of element
Cannot change style with set state conditionally based on type of element

Time:01-30

I am trying to change colors of activity buttons when you click on a zone button with the changeActivityStyleBasedOnZone function. If for instance I click on a zone button that has type "concentration" I want to change the color of the activity buttons to the same color if they have type "concentration". Right now all the activity buttons change color when you click on a zone button. Right now I have the functionality that if you click on an activity button it changes color depending on the type of activity and this is something that I want to keep. How do I make only the buttons with the same type as the clicked zone button change colors without affecting the other activity buttons? I have also tried to test filtering the activities array on type and then mapping through the filtered array to set the state only on the filtered array but maybe this is the wrong approach or I am doing it wrong because the mapping doesn't seem to work.

import React, { useState } from 'react';

const Game = (activityType) => {
 const [activityStyle, setActivityStyle] = useState("activity");
 const [zoneClicked, setZoneClicked] = useState(false);
 const [clickedActivityIndex, setClickedActivityIndex] = useState(-1);

 const zones = [
    {id: 1, name: "Concentration", styleType: "concentration-zone", type: "concentration" },
    {id: 2, name: "Communication", styleType: "communication-zone", type: "communication"},
    {id: 3, name: "Collaboration", styleType: "collaboration-zone",  type: "collaboration"},
    {id: 4, name: "Chill Out", styleType: "chill-out-zone", type: "chillout"},
    {id: 5, name: "Camp", styleType: "camp-zone", type: "camp"}
   ]

 const activities = [
    {id: 1, name: "Code", type: "concentration"},
    {id: 2, name: "Teams Meeting", type: "communication"},
    {id: 3, name: "Make a phone call", type: "camp"},
    {id: 4, name: "Work shops with colleagues", type: "collaboration"},
    {id: 5, name: "Coffee break", type: "chillout"},
    {id: 6, name: "Lively discussions & brainstorming", type: "collaboration"},
   ]

 const changeActivityStyle = (activityType, index) => {
    setClickedActivityIndex(index);
     if (activityType === "concentration") {
         setActivityStyle("activity-concentration");
         setClickedActivityIndex(0) ;
       }
     if (activityType === "communication") {
         setActivityStyle("activity-communication");
     }
     if (activityType === "camp") {
         setActivityStyle("activity-camp");
     }
     if (activityType === "chillout") {
         setActivityStyle("activity-chill-out");
     } 
     if (activityType === "collaboration") {
         setActivityStyle("activity");
     }
     return;
 };

 const changeActivityStyleBasedOnZone = (zoneType) => {
     setZoneClicked(true);
     if (zoneType === "concentration") {
         const concentrationActivities = activities.filter(activity => activity.type === "concentration");
         concentrationActivities.map((ca) => {
            setActivityStyle("activity-concentration");
         }); 
        }
         
     if (zoneType === "communication") {
         setActivityStyle("activity-communication");
     }
     if (zoneType === "camp") {
         setActivityStyle("activity-camp");
     }
     if (zoneType === "chillout") {
         setActivityStyle("activity-chill-out");
     }
     if (zoneType === "collaboration") {
         setActivityStyle("activity");
     }
     return;
 }
return (
        <>
            <section className="header"><h2>Click on an activity box to see which area it belongs to</h2></section>
            <section className="game-area">
                <div className="activity-zone-container">
                    <div className="zone-container">
                        {zones.map((zone =>
                            <button 
                                className="zone" 
                                id={zone.styleType} 
                                key={zone.id}
                                zoneType={zone.type}
                                onClick={() => changeActivityStyleBasedOnZone(zone.type, 
                                 activityType)}
                                >
                                <p>{zone.name}</p>
                            </button>
                         ))}
                    </div>
                    <div className="activity-container">
                        {activities.map((activity, index) => 
                            <button 
                                className={clickedActivityIndex === index | zoneClicked === true ? activityStyle: 'activity'} 
                                onClick={() => changeActivityStyle(activity.type, index)} 
                                key={activity.id}
                                activityType={activity.type}>
                                <p>{activity.name}</p>
                            </button>
                            )}
                    </div>        
                </div>
            </section>
        </>
     );
   };

CodePudding user response:

There are a number of problems here. I will try to address them in a sensible order and then provide example code at the end.

Issues

1. All the buttons are changing color because of this line:

className={clickedActivityIndex === index | zoneClicked === true ? activityStyle: 'activity'}

If zoneClicked is true, the class stored in activityStyle is applied to all buttons. Meanwhile, zoneClicked is set to true as soon as changeActivityStyleBasedOnZone is called:

const changeActivityStyleBasedOnZone = (zoneType) => {
     setZoneClicked(true);
...
}

2. changeActivityStyle and changeActivityStyleBasedOnZone conflict with each other.

They both change activityStyle, so the current value is applied to buttons with matching conditions and overrides the previous class value. This is just logically messy and makes it unclear what the point of changeActivityStyle is. My assumption is you want to apply a different style to the selected activity, independent of the selected zone style.

3. The if blocks in both changeActivityStyle and changeActivityStyleBasedOnZone are unnecessary and complicate the code.

This particular block is problematic. It applies the same value to activityStyle multiple times. I removed it completely from my solution, but I wanted to make you aware of that.

if (zoneType === "concentration") {
      const concentrationActivities = activities.filter(
        (activity) => activity.type === "concentration"
      );
      concentrationActivities.map((ca) => {
        setActivityStyle("activity-concentration");
      });
    }

4. zoneType and activityType are not valid html button properties.

Use data- attributes to create custom properties. The names must be all lowercases. e.g. data-activity-type and data-zone-type


Solution

As I said, I'm not totally sure of your goals, and I don't have your CSS, so you'll need to adjust this for your use case. That said, I think it will get you close.

Here's a CodeSandbox with the full example. I added a table to display the state values.

First, I changed changeActivityStyle and changeActivityStyleBasedOnZone as such:

const changeActivityStyle = (index) => {
    setClickedActivityIndex(index);
  };

  const changeActivityStyleBasedOnZone = (zone) => {
    setZoneClicked(zone);
    return;
  };

You'll notice this means zoneClicked now stores the whole zone object. So change the useState hook to this:

const [zoneClicked, setZoneClicked] = useState({});

And the zone button onClick gets changed to:

onClick={() => changeActivityStyleBasedOnZone(zone)}

Then I changed the activity button className like so:

<button
    className={
        `${activity.type === zoneClicked.type ? zoneClicked.styleType :"activity"} 
        ${clickedActivityIndex === index ? "selected" : ""}`
    }
    onClick={(e) => changeActivityStyle(index)}
    key={activity.id}
    data-activity-type={activity.type}
    >

Using template literals, we can apply two different classes based on two conditions, one related to the selected zone and one related to the selected activity.

Since zoneClicked now stores the whole zone, I just apply the selected zone's styleType as a class name.

If the selected activity index matches, I apply an additional class to highlight the selected button. In this case, the class just removes the border, but you could apply whatever style you want. Note the !important operator in the selected class - this ensures that the zone class doesn't override it if applied later.

CodePudding user response:



Hello can you provide the css. Here is the repl link you can paste it directly. https://replit.com/@pskath1/GrowingIllfatedRate#src/App.jsx

CodePudding user response:

try this link and tell me if that is how the app is supposed to work. https://growingillfatedrate.pskath1.repl.co/

here is the working code

import React,{ useState } from 'react';

const activities = [
    {id: 1, name: "Code", type: "concentration"},
    {id: 2, name: "Teams Meeting", type: "communication"},
    {id: 3, name: "Make a phone call", type: "camp"},
    {id: 4, name: "Work shops with colleagues", type: "collaboration"},
    {id: 5, name: "Coffee break", type: "chill-out"},
    {id: 6, name: "Lively discussions & brainstorming", type: "collaboration"},
];


 const zones = [
    {id: 1, name: "Concentration", styleType: "concentration-zone", type: "concentration" },
    {id: 2, name: "Communication", styleType: "communication-zone", type: "communication"},
    {id: 3, name: "Collaboration", styleType: "collaboration-zone",  type: "collaboration"},
    {id: 4, name: "Chill Out", styleType: "chill-out-zone", type: "chill-out"},
    {id: 5, name: "Camp", styleType: "camp-zone", type: "camp"}
];






const Game = () => {

 const [activityStyle, setActivityStyle] = useState("activity");
 const [zoneClicked, setZoneClicked] = useState(false);
 const [clickedActivityIndex, setClickedActivityIndex] = useState(-1);


 const changeActivityStyle = (activityType, index) => {
    setClickedActivityIndex(index);
     if (activityType === "concentration") {
         setActivityStyle("concentration");
         setClickedActivityIndex(0) ;
      }

     if (activityType === "communication") {
         setActivityStyle("communication");
     }
     if (activityType === "camp") {
         setActivityStyle("camp");
     }
     if (activityType === "chillout") {
         setActivityStyle("chill-out");
     } 
     if (activityType === "collaboration") {
         setActivityStyle("collaboration");
     }
     
     if (activityType === "chill-out") {
         setActivityStyle("chill-out");
     }
     return;
 };

 const changeActivityStyleBasedOnZone = (zoneType) => {
     setZoneClicked(true);
     if (zoneType === "concentration") {
        setActivityStyle("concentration")
      }
         
     if (zoneType === "communication") {
         setActivityStyle("communication");
     }
     if (zoneType === "camp") {
         setActivityStyle("camp");
     }
     if (zoneType === "chill-out") {
         setActivityStyle("chill-out");
     }
     if (zoneType === "collaboration") {
         setActivityStyle("collaboration");
     }
     return;
 }
return (
        <>
            <section className="header"><h2>Click on an activity box to see which area it belongs to</h2></section>
            <section className="game-area">
                <div className="activity-zone-container">
                    <div className="zone-container">
                        {zones.map((zone =>
                            <button 
                                className={zone.type} 
                                id={zone.styleType} 
                                key={zone.id}
                                zoneType={zone.type}
                                onClick={() => changeActivityStyleBasedOnZone(zone.type)}
                                >
                                <p>{zone.name}</p>
                            </button>
                         ))}
                    </div>
                    <div className="activity-container">
                        {activities.map((activity, index) => 
                            <button 
                                className={activityStyle == activity.type ? activityStyle: '' } 
                                onClick={() => { changeActivityStyle(activity.type, index) }} 
                                key={activity.id}
                                activityType={activity.type}>
                                <p>{activity.name}</p>
                            </button>
                            )}
                    </div>        
                </div>
            </section>
        </>
     );
};

and also the same repl link you can add your styles and test on your way. https://replit.com/@pskath1/GrowingIllfatedRate?v=1

  •  Tags:  
  • Related