Hone logo
Hone
Problems

Implementing a React Portals System for Modals

This challenge requires you to build a flexible and reusable modal component in React using TypeScript. Portals are a powerful React feature that allows you to render children into a DOM node outside of the parent component's DOM hierarchy. This is crucial for managing z-index issues and ensuring modals are rendered correctly, regardless of their parent's styling.

Problem Description

Your task is to create a Modal component that can be rendered at any point in your React application's component tree, but will be appended to a designated DOM element (e.g., a div with id="modal-root" in your index.html).

Key Requirements:

  1. Modal Component: Create a Modal component that accepts children as props, which will be the content of the modal.
  2. Portal Rendering: The Modal component must utilize ReactDOM.createPortal to render its children into a specific DOM node.
  3. Target DOM Node: The target DOM node for the portal should be configurable, defaulting to a div with the ID modal-root. You should also allow the user to specify a different target DOM node.
  4. Styling and Structure: The Modal component should provide a basic structure for a modal (e.g., an overlay background and the modal content container). You are not required to implement complex styling, but the basic structure should be present.
  5. Accessibility: Consider basic accessibility for modals (e.g., focus management, though full implementation is not strictly required for this challenge, thinking about it is a plus).
  6. TypeScript: All components and types must be written in TypeScript.

Expected Behavior:

When a Modal component is rendered, its content should appear in the specified target DOM node, overlaying other content on the page. Closing the modal (if a close mechanism is implemented) should remove it from the DOM.

Edge Cases:

  • What happens if the modal-root element doesn't exist in the DOM?
  • How should the Modal component behave if no target DOM node is provided?
  • Handling multiple modals rendered simultaneously.

Examples

Example 1:

Input:

// Assuming a div with id="modal-root" exists in index.html
// And a basic Modal component is implemented

function App() {
  const [isModalOpen, setIsModalOpen] = React.useState(false);

  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>Open Modal</button>
      {isModalOpen && (
        <Modal onClose={() => setIsModalOpen(false)}>
          <h2>My Awesome Modal</h2>
          <p>This is the content of the modal.</p>
        </Modal>
      )}
    </div>
  );
}

Output:

The div with id="modal-root" in index.html will contain the rendered modal content:

<!-- In index.html -->
<div id="modal-root">
  <div class="modal-overlay">
    <div class="modal-content">
      <h2>My Awesome Modal</h2>
      <p>This is the content of the modal.</p>
      <button>Close</button> <!-- Assuming a close button is added -->
    </div>
  </div>
</div>

Explanation:

The App component conditionally renders the Modal. When isModalOpen is true, the Modal component is mounted. ReactDOM.createPortal takes the modal's children and the target DOM node (document.getElementById('modal-root')) and renders the children into that node.

Example 2:

Input:

// Assuming a div with id="custom-portal-container" exists in index.html
// And the Modal component can accept a targetRef prop

const customPortalRef = React.useRef<HTMLDivElement>(null);

useEffect(() => {
  if (customPortalRef.current) {
    // Do something with the ref, ensure it's in the DOM
  }
}, []);

function App() {
  const [isModalOpen, setIsModalOpen] = React.useState(false);

  return (
    <div>
      <div ref={customPortalRef} id="custom-portal-container"></div> {/* This div is the target */}
      <button onClick={() => setIsModalOpen(true)}>Open Custom Modal</button>
      {isModalOpen && (
        <Modal targetRef={customPortalRef} onClose={() => setIsModalOpen(false)}>
          <h3>Custom Target Modal</h3>
          <p>This modal renders to a specific div.</p>
        </Modal>
      )}
    </div>
  );
}

Output:

The div with id="custom-portal-container" will contain the rendered modal content.

Explanation:

This example demonstrates how to pass a ref to the Modal component to specify a custom DOM node for the portal. This is useful for more complex DOM structures or when you need finer control over where portals are rendered.

Constraints

  • The Modal component must be implemented using functional components and React Hooks.
  • The solution must be in TypeScript.
  • The modal-root element should be assumed to exist in the index.html for the default case.
  • The primary focus is on the portal mechanism and component structure, not advanced CSS styling or animations.

Notes

  • You'll likely need to import ReactDOM from 'react-dom'.
  • Consider creating a helper function or hook to manage the portal target DOM node lookup and creation if it doesn't exist.
  • Think about how to handle the onClose event for the modal. A button within the modal content is a common pattern.
  • While full accessibility is not the primary goal, think about what makes a modal accessible. This might involve keyboard navigation, trapping focus, or ARIA attributes.
Loading editor...
typescript