Render a button (when hovered) for only one element of an array, when mapping an array

Viewed 138
const Chat = () => {
const bodyRef = useRef(null)
const [{messages,roomMessages,roomID,socket,user}, dispatch] = useStateValue()
const [dropdown, setDropdown] = useState(false)
useEffect(()=>{
    const setScreen = () =>{
        bodyRef.current.scrollTop = bodyRef.current.scrollHeight
    }

    setScreen()
},[])
const updateMessages = (message) =>{
    const allMessages = messages
    allMessages.push(message)
    dispatch({
        type: "SET_MESSAGES",
        item: allMessages
    })
    bodyRef.current.scrollTop = bodyRef.current.scrollHeight
}
useEffect(() => {
    socket.on('new-message', (message) =>{
        if(messages === null){
            console.log('here it was null')
        } else {
            updateMessages(message)
        }
    })
    // clean up socket on unmount

}, [])
const handleDropdown = (index) =>{
    setDropdown(true)
    console.log(index)
    if(dropdown === true){
        setDropdown(false)
    }
}
return (
        <div className={'chat'}>
            <ChatHeader/>
            <div ref={bodyRef}  className={'chat__body'}>
                {messages?.map((item,index)=>(
                    <Emojify  key={item._id}>
                        <div
                            onMouseEnter={() => handleDropdown(index)}                                 
                            onMouseLeave={handleDropdown}
                            className={`chat__message ${item.name === user.displayName && "chat__reciever"}`}>
                            <h5  className={'chat__messageUser'}>
                                {item.name}
                            </h5>
                            <p className={'chat__messagetext'}>
                                {item.message}
                            </p>
                            {dropdown && <Button><ArrowDropDownIcon/></Button>}
                            {item.img && <img alt={''} className={'chat__img'} src={item.imgURL}/>}
                        </div>
                    </Emojify>
                    ))}
            </div>
            <div className={'chat__footer'}>
                <ChatFooter/>
            </div>
        </div>

);

I want to render the Button {dropdown && <Button><ArrowDropDownIcon/></Button>} when it is hovered over.

Right now if I hover over one div it gets rendered for all other mapped divs but I don't want this; it shoul be for a specific div only.

How would I do this? Does someone have an idea? I passed index but can't seem to use it as I desire.

1 Answers

Instead of storing a single "global" boolean for dropdown open or not (which all mapped elements are reading), you could correlate a specific dropdown you want to be open. It's good you are already passed an index to the handler.

Change state to store null (no dropdown) or an index (show dropdown).

const [dropdown, setDropdown] = useState(null);

Update the handler to store/toggle an index.

const handleDropdown = (index) =>{
  setDropDown(value => value === index ? null : index);
}

In the render simply check if the current index matches the dropdown state.

<div
  onMouseEnter={() => handleDropdown(index)}
  onMouseLeave={() => handleDropdown(index)} // <-- handle index in mouse leave
  className={`chat__message ${
    item.name === user.displayName && "chat__reciever"
  }`}
>
  <h5 className={"chat__messageUser"}>{item.name}</h5>
  <p className={"chat__messagetext"}>{item.message}</p>
  {dropdown === index && ( // <-- check index match with dropdown value
    <Button>
      <ArrowDropDownIcon />
    </Button>
  )}
  {item.img && (
    <img alt={""} className={"chat__img"} src={item.imgURL} />
  )}
</div>
Related