Custom Dropdown Component in Css and React Js

  • A comprehensive guide to creating a flexible and dynamically positioned dropdown component in React. This dropdown will automatically adjust its position based on the available space on the screen, especially useful for situations like being placed on the right side of a navbar to prevent it from extending off-screen. Additionally, the control over the options displayed in the dropdown will reside in the parent component, allowing for a more versatile and reusable component.
Step 1: Create the Dropdown Component
  • First, we'll define our DropdownComponent. This component will accept children (the trigger element for the dropdown) and a showDropdown prop which controls the visibility of the dropdown content. It will also accept dropdownOptions, an array of options to display within the dropdown, passed from the parent component.


    // DropdownComponent.js
    import React, { useRef, useEffect } from 'react';
    import './DropdownComponent.css';

    const DropdownComponent = ({ children, showDropdown, dropdownOptions, onClose }) => {
        const dropdownRef = useRef(null);

        useEffect(() => {
            const handlePosition = () => {
                // Ensure dropdownContent is not attempted to be accessed unless it's visible
                if (showDropdown && dropdownRef.current) {
                    const dropdownContent = dropdownRef.current.querySelector('.dropdown-content');
                    if (dropdownContent) {
                        const rect = dropdownRef.current.getBoundingClientRect();
                        const rightSpace = window.innerWidth - rect.right;

                        if (rightSpace < dropdownContent.offsetWidth) {
                            dropdownContent.style.right = '0';
                            dropdownContent.style.left = 'auto';
                        } else {
                            dropdownContent.style.left = '0';
                            dropdownContent.style.right = 'auto';
                        }
                    }
                }
            };

            handlePosition();

            // Re-calculate position when window is resized or dropdown visibility changes
            window.addEventListener('resize', handlePosition);

            window.addEventListener("click", () => {
                onClose()
            })

            return () => {
                window.removeEventListener('resize', handlePosition);
                window.removeEventListener("click", () => {
                    onClose()
                })
            }
        }, [showDropdown]); // Dependency array now includes showDropdown

        return (
            <div ref={dropdownRef} className="dropdown-container"
                onClick={(e) => {
                    e.stopPropagation()
                }}
            >
                <div className="dropdown-trigger" >
                    {children}
                </div>
                {showDropdown && (
                    <div className="dropdown-content">
                        {dropdownOptions.map((option) => (
                            <p
                                key={option.id}
                                onClick={(e) => {
                                    onClose()
                                    option.onClick({ event: e, option })
                                }}
                            >
                                {option.label}
                            </p>
                        ))}
                    </div>
                )}
            </div>
        );
    };

    export default DropdownComponent;

Step 2: Add CSS for Styling

  • Now, let's add some CSS to style our dropdown, ensuring it can dynamically adjust its position and offering a smooth transition for a better user experience.


    /* DropdownComponent.css */
    :root {
        --dropdown-bg-color: #f9f9f9;
        --dropdown-text-color: #333;
        --dropdown-hover-bg-color: #f1f1f1;
    }

    .dropdown-container {
        display: inline-block;
        position: relative;
    }

    .dropdown-trigger {
        cursor: pointer;
        user-select: none;
    }

    .dropdown-content {
        position: absolute;
        background-color: var(--dropdown-bg-color);
        min-width: 160px;
        box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
        z-index: 1;
        transition: opacity 0.3s ease-in-out, transform 0.3s ease;
        opacity: 0;
        visibility: hidden;
        transform: translateY(-20px);
        right: auto;
        left: 0;
    }

    .dropdown-content {
        opacity: 1;
        visibility: visible;
        transform: translateY(0);
    }

    .dropdown-content p {
        color: var(--dropdown-text-color);
        padding: 12px 16px;
        text-decoration: none;
        display: block;
    }

    .dropdown-content p:hover {
        background-color: var(--dropdown-hover-bg-color);
        cursor: pointer;
    }

Step 3: Implement the Parent Component

  • Finally, we'll use the DropdownComponent within a parent component. This parent component will manage the dropdown's options and its visibility state.


  // App.js or any parent component
  import React, { useState } from 'react';
  import DropdownComponent from './DropdownComponent';

  const App = () => {
    const [showDropdown, setShowDropdown] = useState(false);
    const dropdownOptions = [
      {
        id: 1,
        label: 'Option 1',
        value: 1,
        onClick: (props) => {
          console.log("option clicked 1", props)
        }
      },
      {
        id: 2,
        label: 'Option 2',
        value: 2,
        onClick: () => {
          console.log("option clicked 2")
        }
      },
      {
        id: 3,
        label: 'Option 3',
        value: 3,
        onClick: () => {
          console.log("option clicked 3")
        }
      }
    ];

    return (
      <div>
        <DropdownComponent
          showDropdown={showDropdown}
          dropdownOptions={dropdownOptions}
          onClose={() => setShowDropdown(false)}
        >
          <button onClick={() => setShowDropdown(!showDropdown)}>Toggle Dropdown</button>
        </DropdownComponent>
      </div>
    );
  };

  export default App;

Adjustments Made:
  • Positioning Logic inside useEffect: The positioning logic now also runs when showDropdown changes, ensuring the dropdown content is available for position calculations.
  • Checking for dropdownContent: Before attempting to access dropdownContent.offsetWidth, we ensure dropdownContent exists. This prevents trying to read properties of null.
  • Added showDropdown to useEffect Dependencies: Including showDropdown in the dependencies of useEffect ensures that the position handling logic runs not only on component mount and window resize but also whenever the visibility of the dropdown changes.
  • This approach should resolve the error and ensure that the dropdown positions itself correctly without attempting to access properties of elements that may not be rendered yet.
  • This setup creates a reusable dropdown component in React that not only offers flexibility in terms of the dropdown options but also intelligently positions itself based on the available viewport space. The control over the dropdown, including its visibility and options, is managed by the parent component, making it a versatile tool for various use cases.

No comments:

Post a Comment