Creating Multiple Instances of a Dialog Tree in React.js
Tags: react
Published: 2023-10-06 12:46:25

**Description:**
I'm working on a React.js application where I have a dialog tree that I need to open multiple times. Each time I open it, I want to create a new instance of the dialog tree. However, I'm encountering issues with this behavior. Instead of getting separate instances, the dialogs seem to be sharing some state. 

**Code Snippet:**
`import React, { useCallback, useEffect } from 'react';
import arrowBackSvg from '../Images/arrow-back.svg';

const DialogTree = React.memo(({ bubble: { id, items }, onClickBubble, isOpen }) => {
    const handleClosePopup = () => {
        onClickBubble(null, null); // Notify the parent to close the dialog
    };

    const handleOnClick = useCallback((reference, title) => {
        handleClosePopup();
        onClickBubble(reference, title);
    }, [onClickBubble]);

    const renderLink = (item) => {
        const { id: itemId, title, kb_content_link } = item;
        const { reference } = kb_content_link.target;
        return (
            <button
                className='btn btn-1'
                title={item.title}
                onClick={() => handleOnClick(reference, title)}
            >
                {title}
            </button>
        );
    };

    const renderText = (item) => {
        const { id: itemId, title } = item;
        return (
            <button
                className='btn btn-2'
                title={title}
                onClick={() => handleOnClick(null, title)}
            >
                {title}
            </button>
        );
    };

    const renderDialogTreeContainer = () => {
        return items.map((item, index) => {
            if (item.type === 'link') {
                return renderLink(item);
            } else {
                return renderText(item);
            }
        });
    };

    useEffect(() => {
        if (isOpen) {
            // Add event listener to handle closing when the user clicks outside the dialog
            const handleOutsideClick = (e) => {
                if (!e.target.closest('.dialog-tree-bubble-container')) {
                    handleClosePopup();
                }
            };

            document.addEventListener('mousedown', handleOutsideClick);

            return () => {
                // Remove the event listener when the component unmounts
                document.removeEventListener('mousedown', handleOutsideClick);
            };
        }
    }, [isOpen]);


    return (
        <div>
            {isOpen && (
                <div className={`dialog-tree-bubble-container`}>
                    <div className='popup-content'>
                        <div className='button-container-wrapper'>
                            <div className='button-container'>
                                <div className='button-list'>
                                    <div className='close-button' onClick={handleClosePopup}>
                                        <img src={arrowBackSvg} alt={'Arrow Back'} />
                                    </div>
                                    {renderDialogTreeContainer()}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            )}
        </div>
    );
});

export default DialogTree;


import React, { useEffect, useId, useState } from 'react';
import PropTypes from 'prop-types';
import DialogTreeBubble from './DialogTree/Index';

export {
   
    DialogTreeBubble,
    
};


const Index = ({ author, bubbles, isSatisfied, suggestions, message, callbackMethod, scrollToBottom,handleDialogClose,isDialogOpen }) => {
    const [satisfactionMessage, setSatisfactionMessage] = useState('');
    const id = useId();

    const handleSatisfactionSubmit = (satisfied) => {
        scrollToBottom();
        setSatisfactionMessage(satisfied ? 'Thank you for your feedback!' : 'We apologize for any inconvenience caused.');
    };

    const handleLinkClick = (reference, title) => {
        callbackMethod(reference, title);
    };

    const getBubbleComponent = (bubble, index) => {
        switch (bubble.type) {
            
            case BUBBLES.DialogTree:
                return (<div>
                           {isDialogOpen && <DialogTreeBubble   id={id}
                                                                bubble={bubble}
                                                                onClickBubble={handleDialogClose}
                                                                isOpen={isDialogOpen}
                             />}
                       </div>);
           
            default:
                return '';
        }
    };

    const renderBotMessage = () => {
        return (<>
                {bubbles && bubbles.length > 0 && (bubbles.map((bubble, index) => (
                        <div className={`message message-${author}`} key={UUID()}>
                            {getBubbleComponent(bubble, index)}
                        </div>)))}
               
              
            </>);
    };

    const renderUserMessage = () => {
        return <div className={`message message-${author}`} key={UUID()}>
            <div className='message-content'>
                {message}
            </div>
        </div>;
    };


    return author === MESSAGE_SENDER.Bot ? renderBotMessage() : renderUserMessage();
};

Index.propTypes = {
    author: PropTypes.string.isRequired,
    bubbles: PropTypes.array.isRequired,
    isSatisfied: PropTypes.bool,
    suggestions: PropTypes.array,
    message: PropTypes.string,
    callbackMethod: PropTypes.func.isRequired,
    scrollToBottom: PropTypes.func,
};

export default Index;
`

Problem: When I open the dialog tree multiple times, they don't behave as separate instances. What am I missing here? How can I achieve the desired behavior?


@appreactjs @react