Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I hide and show components with useState or conditional rendering in React?

My initial goal to solve:

  • I have 4 components lined up next to each other. I want to be able to toggle/onClick one of the components and have the other 3 components disappear. Then re-toggling that same component to have the other components reappear. But also have the ability to do the same task with toggling any of the other components.
  • Each of components in simple terms is a div tag with an image tag within.

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!

like image 258
Patrik Kozak Avatar asked Nov 06 '22 03:11

Patrik Kozak


1 Answers

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;
like image 198
Mantas Astra Avatar answered Nov 14 '22 23:11

Mantas Astra