Reputation: 55
My initial goal to solve:
My initial take on the problem:
export const Test = () => {
const intialValue = [{id: 0, component: null}]
const array = [
{id: 1, component: <CompOne/>},
{id: 2, component: <CompTwo/>},
{id: 3, component: <CompThree/>},
{id: 4, component: <CompFour/>}
]
const [chosenNumber, setChosen] = useState(0)
const [statearray, setArray] = useState(array)
useEffect(() => {
console.log(chosenNumber)
const arr = array
if(chosenNumber === 0 ) setArray(array)
else setArray(arr.filter( num => num === chosenNumber))
}, [chosenNumber])
const handleClick = (number) => {
if(number === chosenNumber) setChosen(0)
else setChosen(number)
}
return (
<div className="flex" >
{statearray.map((comp, number) => <div key={number} className="h-12 w-12 bg-gray-400 m-1 flex items-center justify-center" onClick={() => handleClick(comp.id)}>{comp.id}</div>)}
</div>
);
}
My thought process is when I click the component (div), I'll set the chosenNumber, if the div I clicked is the same as chosen, reset chosen to zero.
Every time the chosenNumber changes, useEffect should detect it and filter the array with chosenNumber, if chose is zero, reset stateArray.
Right now when I click one of the components, they all disappear. I am just not sure if using zero is the right thing to use to compare each object or what I need to use instead.
Thanks for the help!
Upvotes: 5
Views: 3660
Reputation: 1062
If I were you, I would simplify things. First, I would extract array
from the component as you don't want that to be re-rendered every time a component is re-rendered. Then, I would change your state and leave only the items
state that will contain array
items. I would also extend the array
items by providing a flag isVisible
. Then, I would remove your useEffect
and improve the handleClick
as you want to trigger this only when a button is clicked. In the handleClick
function, I would create a new set of items by mapping through your items
and changing "not clicked" items isVisible
to false. This way, you know which items to hide. Lastly, I would render the components based on the isVisible
attribute. So if isVisible
is true, the item will be rendered with hidden
set to false
and vice versa.
This way the code is much simpler, more performant and easier to understand. Plus, it does what you asked.
Here's the link of the example working code: codesandbox
import React, { useState } from "react";
const Comp1 = () => <div>hi</div>;
const Comp2 = () => <div>hi2</div>;
const Comp3 = () => <div>hi3</div>;
const array = [
{ id: 1, component: <Comp1 />, isVisible: true },
{ id: 2, component: <Comp2 />, isVisible: true },
{ id: 3, component: <Comp3 />, isVisible: true }
];
export const Test = () => {
const [items, setItems] = useState(array);
const handleClick = (number) => {
const triggeredItems = items.map((item) => {
if (item.id !== number) {
item.isVisible = !item.isVisible;
}
return item;
});
setItems(triggeredItems);
};
return (
<div className="flex">
{items.map(({ id, component, isVisible }) => (
<div
key={id}
className="h-12 w-12 bg-gray-400 m-1 flex items-center justify-center"
onClick={() => handleClick(id)}
hidden={!isVisible}
>
{component}
</div>
))}
</div>
);
};
export default Test;
Upvotes: 1
Reputation: 331
const [currentComponent, setCurrentComponent] = useState(1);
const handleClick = () =>{
switch(currentComponent){
case 1:
setCurrentComponent(2)
case 2:
setCurrentComponent(3)
case 3:
setCurrentComponent(4)
case 4:
setCurrentComponent(1)
}
}
<button onClick={()=> handleClick()}>Click me!</button>
{currentComponent == 1 && <Comp1/>};
{currentComponent == 2 && <Comp2/>};
{currentComponent == 3 && <Comp3/>};
{currentComponent == 4 && <Comp4/>};
Upvotes: 0
Reputation: 324
One simple way is to have a boolean saved in state (such as isVisible = true ) in either useState or mobx etc. Then in the react component you can use a double ampersand, like below:
{isVisible && <MyComponent/>}
This will then only show if isVisible = true
Upvotes: 0