Home > Mobile >  How to set hover in a React loop and effect only one instead of all elements in the loop?
How to set hover in a React loop and effect only one instead of all elements in the loop?

Time:08-25

When I use setHover it reflects to all list data which returned from map loop. How can I use hover to reflect on itself element?

  const [hover, setHover] = useState(true)

  function MouseOver(event) {
    setHover(true)
  }
  function MouseOut(event){
    setHover(false)
  }

  {data.map((item, index) => (

    //When I hover parent div I want to show the {item.arrow} div inside and not all {item.arrow} divs in the loop

    <div key={index} onm ouseEnter={MouseOver} onm ouseLeave={MouseOut} className="flex gap-3">
      <div>
        {item.content}
      </div>
      
      <div hidden={hover}>
        {item.arrow}
      </div>
    </div>
  ))}

CodePudding user response:

If the state does not need to be controlled by the parent you can create a new component to use in the list.

Each component will then control its own hover state.

const List = ({data}) => {
    return (
        <div>
        {
            data.map((item, index) => (<Item key={index} item={item} />))
        }
        </div>
    )
}

const Item = ({item}) => {
    const [hover, setHover] = useState(true)

    const mouseOver = (event) => {
        setHover(true)
    }

    const mouseOut = (event) => {
        setHover(false)
    }

    return (
        <div onm ouseEnter={mouseOver} onm ouseLeave={mouseOut} className="flex gap-3">
            <div>
                {item.content}
            </div>
            <div hidden={hover}>
                {item.arrow}
            </div>
        </div>
    );
}

If the state does need to be controlled by the parent you can use a Record<number, boolean> to store the states.

const List = ({data}) => {
    const [hover, setHover] = useState({})

    const mouseOver = (event, index) => {
        setHover(c => {
            return {
                ...c,
                [index]: true
            };
        })
    }

    const mouseOut = (event, index) => {
        setHover(c => {
            return {
                ...c,
                [index]: false
            };
        })
    }

    return (
        <div>
        {
            data.map((item, index) => (
                <div 
                    key={index} 
                    onm ouseEnter={(e) => {
                        mouseOver(e, index);
                    }} 
                    onm ouseLeave={(e) => {
                        mouseOut(e, index);
                    }} 
                    className="flex gap-3"
                >
                    <div>
                        {item.content}
                    </div>
                    <div hidden={hover[index]}>
                        {item.arrow}
                    </div>
                </div>
            ))
        }
        </div>
    )
}

If the state is not needed for anything other than hiding a div you could also just use CSS.

CSS will not require the component to rerender everytime you hover over it.

CSS

.hoverable-show {
    display: none;
}

.hoverable-item:hover .hoverable-show {
    display: block;
}

JS

const List = ({data}) => {
    return (
        <div>
        {
            data.map((item, index) => (
                <div
                    className="flex gap-3 hoverable-item"
                >
                    <div>
                        {item.content}
                    </div>
                    <div className="hoverable-show">
                        {item.arrow}
                    </div>
                </div>
            ))
        }
        </div>
    )
}

Preference should be CSS -> Individual State -> Parent (list) State.

CodePudding user response:

This looks like a use case for the useReducer hook available right from the react library.

  • Related