Problem conditionally showing buttons based on sibling’s height

OBJECTIVE

I’m developing a UI chat interface as a project to learn React + TS. When a chat message is long enough and so provides enough vertical space, its action buttons (such as copy message, share, etc.) should appear vertically stacked to the right of the message’s container. When there isn’t enough space, meaning the stacked buttons would be taller than the message’s container, the buttons shouldn’t appear at all:

enter image description here

PROBLEM

The code below isn’t working. The buttons still appear as normal. Strangely, when the window is resized, the button’s visibility toggles and they either disappear/reappear depending on whether they are currently showing (because of React’s hot reload feature, either state can be the starting one).

WHAT I HAVE TRIED

  • Using Lodash to both throttle and debounce the function
  • Using useLayoutEffect instead of useEffect
  • Using resize observers instead
  • Adding a very long time/debounce/throttle delay
  • Using getClientRects instead of offsetHeight
  • Talking to ChatGPT 4, Phind, Gemini and Blackbox
import React, { useLayoutEffect, useRef, useState, useCallback } from "react";
import lodash from 'lodash';

// import statements removed for brevity
// props interface removed for brevity

const Message: React.FC<MessageProps> = ({ sender, messageContents, actionButtons, children }) => {
    const chatMessageRef = useRef<HTMLDivElement>(null);
    const sideButtonsRef = useRef<HTMLDivElement>(null);
    const [showSideButtons, setShowSideButtons] = useState(true);

    const checkAndSetButtonVisibility = useCallback(() => {
        const messageContainer = chatMessageRef.current;
        const sideButtons = sideButtonsRef.current;

        if (messageContainer && sideButtons) {
            setShowSideButtons(messageContainer.offsetHeight > sideButtons.offsetHeight);
        }
    }, []);

    // Using lodash's throttle function to limit how often the checkAndSetButtonVisibility function can be invoked
    const debouncedCheck = useCallback(lodash.debounce(checkAndSetButtonVisibility, 1500), [checkAndSetButtonVisibility]);

    useLayoutEffect(() => {
        window.addEventListener('resize', debouncedCheck);
        checkAndSetButtonVisibility(); // Initial check for the correct state

        return () => {
            window.removeEventListener('resize', debouncedCheck);
            debouncedCheck.cancel(); // Cancel any pending execution of the throttled function on cleanup
        };
    }, [debouncedCheck, checkAndSetButtonVisibility]);

    return (
        <div className={`chat-message-outer-container ${sender.toLowerCase()}`}>
            <div className="chat-message-inner-container">
                <div className="chat-message-info">
                    {sender === MessageSender.Agent ? (
                        <div className="chat-list-item-ai-model-logo-message">
                            <img className={"message-avatar"} src={aiLogo} alt="AI Model Logo" />
                        </div>
                    ) : (
                        <div className="account-avatar-message">
                            <img className={"message-avatar"} src={userAvatar} alt="User avatar" />
                        </div>
                    )}
                    <div className="chat-message-info-sender">{sender}</div>
                </div>

                <div className="chat-message-select-checkbox">
                    <label className="button checkbox-field">
                        <input type="checkbox"/>
                        <span>
                      <br/>
                    </span>
                    </label>
                </div>

                <div className="chat-message-wrapper">
                    <div ref={chatMessageRef} className={`chat-message ${sender.toLowerCase()}-message`}>
                        <div className="chat-message-contents">{messageContents}</div>
                    </div>
                </div>

                <div
                    ref={sideButtonsRef}
                    className="chat-message-buttons-side"
                    style={{ display: showSideButtons ? 'flex' : 'none' }} // Use the state to control visibility
                >
                    {actionButtons}
                </div>

                <div className="chat-message-bottom">
                    {children}

                    <div className="chat-message-action-row">
                        <div className="chat-message-tag-list">
                            <Tag tagName={'Tag name'}/>

                            <AddElementButton type={"new-tag-message"}/>
                        </div>

                        <div className="chat-message-buttons-bottom">
                            {actionButtons}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
};

export default Message;