DEV Community

longchun
longchun

Posted on

Refactoring React Modals: From Imperative Handles to Declarative Props for Cleaner Code

๐…๐ซ๐จ๐ฆ ๐ˆ๐ฆ๐ฉ๐ž๐ซ๐š๐ญ๐ข๐ฏ๐ž ๐‡๐š๐ง๐๐ฅ๐ž๐ฌ ๐ญ๐จ ๐ƒ๐ž๐œ๐ฅ๐š๐ซ๐š๐ญ๐ข๐ฏ๐ž ๐๐ซ๐จ๐ฉ๐ฌ ๐Ÿ๐จ๐ซ ๐‚๐ฅ๐ž๐š๐ง๐ž๐ซ ๐‚๐จ๐๐ž

๐–ฌ๐–บ๐—‡๐–บ๐—€๐—‚๐—‡๐—€ ๐—†๐—ˆ๐–ฝ๐–บ๐—… ๐—๐—‚๐—Œ๐—‚๐–ป๐—‚๐—…๐—‚๐—๐—’ ๐—‚๐—‡ ๐–ฑ๐–พ๐–บ๐–ผ๐— ๐–ผ๐—ˆ๐—†๐—‰๐—ˆ๐—‡๐–พ๐—‡๐—๐—Œ ๐—‚๐—Œ ๐–บ ๐–ผ๐—ˆ๐—†๐—†๐—ˆ๐—‡ ๐—๐–บ๐—Œ๐—„.
๐–ถ๐—๐—‚๐—…๐–พ ๐—Ž๐—Œ๐–พ๐–จ๐—†๐—‰๐–พ๐—‹๐–บ๐—๐—‚๐—๐–พ๐–ง๐–บ๐—‡๐–ฝ๐—…๐–พ ๐–ผ๐—ˆ๐—†๐–ป๐—‚๐—‡๐–พ๐–ฝ ๐—๐—‚๐—๐— ๐–ฟ๐—ˆ๐—‹๐—๐–บ๐—‹๐–ฝ๐–ฑ๐–พ๐–ฟ ๐—ˆ๐–ฟ๐–ฟ๐–พ๐—‹๐—Œ ๐–บ ๐—‰๐—ˆ๐—๐–พ๐—‹๐–ฟ๐—Ž๐—… ๐—๐–บ๐—’ ๐—๐—ˆ ๐–พ๐—‘๐—‰๐—ˆ๐—Œ๐–พ ๐—†๐–พ๐—๐—๐—ˆ๐–ฝ๐—Œ ๐–ฟ๐—‹๐—ˆ๐—† ๐–บ ๐–ผ๐—๐—‚๐—…๐–ฝ ๐–ผ๐—ˆ๐—†๐—‰๐—ˆ๐—‡๐–พ๐—‡๐— ๐—๐—ˆ ๐—‚๐—๐—Œ ๐—‰๐–บ๐—‹๐–พ๐—‡๐—, ๐—‚๐— ๐—ˆ๐–ฟ๐—๐–พ๐—‡ ๐—‚๐—‡๐—๐—‹๐—ˆ๐–ฝ๐—Ž๐–ผ๐–พ๐—Œ ๐–บ๐—‡ ๐—‚๐—†๐—‰๐–พ๐—‹๐–บ๐—๐—‚๐—๐–พ ๐–ผ๐—ˆ๐—‡๐—๐—‹๐—ˆ๐—… ๐–ฟ๐—…๐—ˆ๐— ๐—๐—๐–บ๐— ๐–ผ๐–บ๐—‡ ๐–ป๐–พ ๐—…๐–พ๐—Œ๐—Œ ๐—‚๐—‡๐—๐—Ž๐—‚๐—๐—‚๐—๐–พ ๐—๐—๐–บ๐—‡ ๐–ฑ๐–พ๐–บ๐–ผ๐—'๐—Œ ๐–ฝ๐–พ๐–ผ๐—…๐–บ๐—‹๐–บ๐—๐—‚๐—๐–พ ๐—‰๐–บ๐—‹๐–บ๐–ฝ๐—‚๐—€๐—†.

๐ˆ๐ฆ๐ฉ๐ž๐ซ๐š๐ญ๐ข๐ฏ๐ž ๐‚๐จ๐ง๐ญ๐ซ๐จ๐ฅ ๐ฐ๐ข๐ญ๐ก ๐šžฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒฬฒ๐™ธฬฒฬฒ๐š–ฬฒฬฒ๐š™ฬฒฬฒ๐šŽฬฒฬฒ๐š›ฬฒฬฒ๐šŠฬฒฬฒ๐šฬฒฬฒ๐š’ฬฒฬฒ๐šŸฬฒฬฒ๐šŽฬฒฬฒ๐™ทฬฒฬฒ๐šŠฬฒฬฒ๐š—ฬฒฬฒ๐šฬฒฬฒ๐š•ฬฒฬฒ๐šŽฬฒ

The initial pattern uses ฬฒ๐šฬฒฬฒ๐š˜ฬฒฬฒ๐š›ฬฒฬฒ๐š ฬฒฬฒ๐šŠฬฒฬฒ๐š›ฬฒฬฒ๐šฬฒฬฒ๐šฬฒฬฒ๐šŽฬฒฬฒ๐šฬฒ to pass a ref down to the ResultModal component. Inside the component, ๐šžฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒฬฒ๐™ธฬฒฬฒ๐š–ฬฒฬฒ๐š™ฬฒฬฒ๐šŽฬฒฬฒ๐š›ฬฒฬฒ๐šŠฬฒฬฒ๐šฬฒฬฒ๐š’ฬฒฬฒ๐šŸฬฒฬฒ๐šŽฬฒฬฒ๐™ทฬฒฬฒ๐šŠฬฒฬฒ๐š—ฬฒฬฒ๐šฬฒฬฒ๐š•ฬฒฬฒ๐šŽฬฒ then allows us to define specific methods (like ฬฒ๐š˜ฬฒฬฒ๐š™ฬฒฬฒ๐šŽฬฒฬฒ๐š—ฬฒ() and ฬฒ๐šŒฬฒฬฒ๐š•ฬฒฬฒ๐š˜ฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒ()) that can be called directly from the parent via that ฬฒ๐š›ฬฒฬฒ๐šŽฬฒฬฒ๐šฬฒ.

// Parent component would call:
// modalRef.current.open();
// modalRef.current.close();
Enter fullscreen mode Exit fullscreen mode

๐ƒ๐ž๐œ๐ฅ๐š๐ซ๐š๐ญ๐ข๐ฏ๐ž ๐‚๐จ๐ง๐ญ๐ซ๐จ๐ฅ ๐ฐ๐ข๐ญ๐ก ๐๐ซ๐จ๐ฉ๐ฌ

๐–ฅ๐—ˆ๐—‹ ๐—Œ๐—‚๐—†๐—‰๐—…๐–พ ๐—Œ๐—๐—ˆ๐—/๐—๐—‚๐–ฝ๐–พ ๐–ฟ๐—Ž๐—‡๐–ผ๐—๐—‚๐—ˆ๐—‡๐–บ๐—…๐—‚๐—๐—’, ๐–บ ๐—†๐—ˆ๐—‹๐–พ '๐–ฑ๐–พ๐–บ๐–ผ๐—-๐—’' ๐—๐–บ๐—’ ๐—‚๐—Œ ๐—๐—ˆ ๐–ผ๐—ˆ๐—‡๐—๐—‹๐—ˆ๐—… ๐—๐—๐–พ ๐—†๐—ˆ๐–ฝ๐–บ๐—…'๐—Œ ๐—๐—‚๐—Œ๐—‚๐–ป๐—‚๐—…๐—‚๐—๐—’ ๐—๐—๐—‹๐—ˆ๐—Ž๐—€๐— ๐—‰๐—‹๐—ˆ๐—‰๐—Œ. ๐–ถ๐–พ ๐—‚๐—‡๐—๐—‹๐—ˆ๐–ฝ๐—Ž๐–ผ๐–พ ๐–บ๐—‡ ๐š’ฬฒ๐—Œ๐–ฎ๐—‰๐–พ๐—‡ ๐—‰๐—‹๐—ˆ๐—‰ ๐—๐—ˆ ๐–ฝ๐—‚๐–ผ๐—๐–บ๐—๐–พ ๐—๐—๐–พ๐—๐—๐–พ๐—‹ ๐—๐—๐–พ ๐—†๐—ˆ๐–ฝ๐–บ๐—… ๐—Œ๐—๐—ˆ๐—Ž๐—…๐–ฝ ๐–ป๐–พ ๐—ˆ๐—‰๐–พ๐—‡ ๐—ˆ๐—‹ ๐–ผ๐—…๐—ˆ๐—Œ๐–พ๐–ฝ, ๐–บ๐—‡๐–ฝ ๐–บ๐—‡ ๐—ˆ๐—‡๐–ข๐—…๐—ˆ๐—Œ๐–พ ๐—‰๐—‹๐—ˆ๐—‰ ๐—๐—ˆ ๐—๐–บ๐—‡๐–ฝ๐—…๐–พ ๐—๐—๐–พ ๐–ผ๐—…๐—ˆ๐—Œ๐—‚๐—‡๐—€ ๐–พ๐—๐–พ๐—‡๐— ๐–ฟ๐—‹๐—ˆ๐—† ๐—๐—‚๐—๐—๐—‚๐—‡ ๐—๐—๐–พ ๐—†๐—ˆ๐–ฝ๐–บ๐—….

import { useRef, useEffect } from "react";

export default function ResultModal({ result, targetTime, isOpen, onClose }) {
  const dialogRef = useRef();

  // Crucially, we use useEffect to synchronize the modal's state
  // with the `isOpen` prop.
  useEffect(() => {
    if (isOpen) {
      // The HTML <dialog> element's showModal() method opens the modal
      // in a top-layer, blocking state.
      dialogRef.current?.showModal();
    } else {
      // The close() method dismisses the modal.
      dialogRef.current?.close();
    }
  }, [isOpen]); // Dependency array: This effect runs ONLY when `isOpen` changes.

  return (
    <dialog ref={dialogRef} className="result-modal" onClose={onClose}>
      <h2>You {result}</h2>
      <p>The target time was <strong>{targetTime} seconds.</strong></p>
      <p>You stopped the timer with <strong>X seconds left.</strong></p>
      <form method="dialog">
        <button onClick={onClose}>Close</button>
      </form>
    </dialog>
  );
}
Enter fullscreen mode Exit fullscreen mode

๐๐ซ๐จ๐ฉ-๐๐š๐ฌ๐ž๐ ๐€๐ฉ๐ฉ๐ซ๐จ๐š๐œ๐ก:

  1. ๐˜‹๐˜ฆ๐˜ค๐˜ญ๐˜ข๐˜ณ๐˜ข๐˜ต๐˜ช๐˜ท๐˜ฆ ๐˜Š๐˜ฐ๐˜ฏ๐˜ต๐˜ณ๐˜ฐ๐˜ญ: ๐–ณ๐—๐–พ ๐—‰๐–บ๐—‹๐–พ๐—‡๐— ๐–ผ๐—ˆ๐—†๐—‰๐—ˆ๐—‡๐–พ๐—‡๐— ๐–ฝ๐–พ๐–ผ๐—…๐–บ๐—‹๐–พ๐—Œ ๐—๐—๐–พ ๐–ฝ๐–พ๐—Œ๐—‚๐—‹๐–พ๐–ฝ ๐—Œ๐—๐–บ๐—๐–พ (๐—‚๐—Œ๐–ฎ๐—‰๐–พ๐—‡: ๐—๐—‹๐—Ž๐–พ ๐—ˆ๐—‹ ๐—‚๐—Œ๐–ฎ๐—‰๐–พ๐—‡: ๐–ฟ๐–บ๐—…๐—Œ๐–พ
  2. ๐˜Œ๐˜น๐˜ฑ๐˜ญ๐˜ช๐˜ค๐˜ช๐˜ต ๐˜‹๐˜ข๐˜ต๐˜ข ๐˜๐˜ญ๐˜ฐ๐˜ธ: ๐–ณ๐—๐–พ ๐–ฟ๐—…๐—ˆ๐— ๐—ˆ๐–ฟ ๐–ฝ๐–บ๐—๐–บ ๐—‚๐—Œ ๐–ผ๐—…๐–พ๐–บ๐—‹: ๐š’ฬฒฬฒ๐šœฬฒฬฒ๐™พฬฒฬฒ๐š™ฬฒฬฒ๐šŽฬฒฬฒ๐š—ฬฒ ๐–บ๐—‡๐–ฝ ๐š˜ฬฒฬฒ๐š—ฬฒฬฒ๐™ฒฬฒฬฒ๐š•ฬฒฬฒ๐š˜ฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒ ๐–บ๐—‹๐–พ ๐–พ๐—‘๐—‰๐—…๐—‚๐–ผ๐—‚๐—๐—…๐—’ ๐—‰๐–บ๐—Œ๐—Œ๐–พ๐–ฝ ๐–ฝ๐—ˆ๐—๐—‡ ๐–บ๐—Œ ๐—‰๐—‹๐—ˆ๐—‰๐—Œ.
  3. ๐˜š๐˜บ๐˜ฏ๐˜ค๐˜ฉ๐˜ณ๐˜ฐ๐˜ฏ๐˜ช๐˜ป๐˜ข๐˜ต๐˜ช๐˜ฐ๐˜ฏ ๐˜ธ๐˜ช๐˜ต๐˜ฉ ๐˜ถ๐˜ด๐˜ฆ๐˜Œ๐˜ง๐˜ง๐˜ฆ๐˜ค๐˜ต: ๐–ณ๐—๐–พ ๐—Ž๐—Œ๐–พ๐–ค๐–ฟ๐–ฟ๐–พ๐–ผ๐— ๐—๐—ˆ๐—ˆ๐—„ ๐—‚๐—Œ ๐—๐—‚๐—๐–บ๐—… ๐—๐–พ๐—‹๐–พ. ๐–จ๐— ๐–พ๐—‡๐—Œ๐—Ž๐—‹๐–พ๐—Œ ๐—๐—๐–บ๐— ๐—๐—๐–พ ๐šœฬฒฬฒ๐š‘ฬฒฬฒ๐š˜ฬฒฬฒ๐š ฬฒฬฒ๐™ผฬฒฬฒ๐š˜ฬฒฬฒ๐šฬฒฬฒ๐šŠฬฒฬฒ๐š•ฬฒ() ๐—ˆ๐—‹ ๐šŒฬฒฬฒ๐š•ฬฒฬฒ๐š˜ฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒ() ๐—†๐–พ๐—๐—๐—ˆ๐–ฝ๐—Œ ๐—ˆ๐–ฟ ๐—๐—๐–พ ๐—‡๐–บ๐—๐—‚๐—๐–พ ๐–ง๐–ณ๐–ฌ๐–ซ <ฬฒ๐šฬฒฬฒ๐š’ฬฒฬฒ๐šŠฬฒฬฒ๐š•ฬฒฬฒ๐š˜ฬฒฬฒ๐šฬฒ>
  4. ๐˜“๐˜ฆ๐˜ท๐˜ฆ๐˜ณ๐˜ข๐˜จ๐˜ช๐˜ฏ๐˜จ ๐˜•๐˜ข๐˜ต๐˜ช๐˜ท๐˜ฆ <๐˜ฅ๐˜ช๐˜ข๐˜ญ๐˜ฐ๐˜จ>: This approach effectively uses the built-in functionality of the HTML <ฬฒ๐šฬฒฬฒ๐š’ฬฒฬฒ๐šŠฬฒฬฒ๐š•ฬฒฬฒ๐š˜ฬฒฬฒ๐šฬฒ> element.

๐–ถ๐—๐—‚๐—…๐–พ ๐šžฬฒฬฒ๐šœฬฒฬฒ๐šŽฬฒฬฒ๐™ธฬฒฬฒ๐š–ฬฒฬฒ๐š™ฬฒฬฒ๐šŽฬฒฬฒ๐š›ฬฒฬฒ๐šŠฬฒฬฒ๐šฬฒฬฒ๐š’ฬฒฬฒ๐šŸฬฒฬฒ๐šŽฬฒฬฒ๐™ทฬฒฬฒ๐šŠฬฒฬฒ๐š—ฬฒฬฒ๐šฬฒฬฒ๐š•ฬฒฬฒ๐šŽฬฒ ๐—๐–บ๐—Œ ๐—‚๐—๐—Œ ๐—…๐–พ๐—€๐—‚๐—๐—‚๐—†๐–บ๐—๐–พ ๐—Ž๐—Œ๐–พ ๐–ผ๐–บ๐—Œ๐–พ๐—Œ (๐–พ.๐—€., ๐–พ๐—‘๐—‰๐—ˆ๐—Œ๐—‚๐—‡๐—€ ๐–บ๐—‡ ๐– ๐–ฏ๐–จ ๐–ฟ๐—ˆ๐—‹ ๐–ฟ๐—ˆ๐–ผ๐—Ž๐—Œ๐—‚๐—‡๐—€ ๐–พ๐—…๐–พ๐—†๐–พ๐—‡๐—๐—Œ, ๐—๐—‹๐—‚๐—€๐—€๐–พ๐—‹๐—‚๐—‡๐—€ ๐–บ๐—‡๐—‚๐—†๐–บ๐—๐—‚๐—ˆ๐—‡๐—Œ), ๐–ฟ๐—ˆ๐—‹ ๐—Œ๐—‚๐—†๐—‰๐—…๐–พ ๐—Œ๐—๐–บ๐—๐–พ-๐–ฝ๐—‹๐—‚๐—๐–พ๐—‡ ๐—‚๐—‡๐—๐–พ๐—‹๐–บ๐–ผ๐—๐—‚๐—ˆ๐—‡๐—Œ ๐—…๐—‚๐—„๐–พ ๐—Œ๐—๐—ˆ๐—๐—‚๐—‡๐—€/๐—๐—‚๐–ฝ๐—‚๐—‡๐—€ ๐–บ ๐—†๐—ˆ๐–ฝ๐–บ๐—…, ๐–บ ๐—‰๐—‹๐—ˆ๐—‰-๐–ป๐–บ๐—Œ๐–พ๐–ฝ ๐–บ๐—‰๐—‰๐—‹๐—ˆ๐–บ๐–ผ๐— ๐—ˆ๐–ฟ๐—๐–พ๐—‡ ๐—…๐–พ๐–บ๐–ฝ๐—Œ ๐—๐—ˆ ๐—†๐—ˆ๐—‹๐–พ ๐—‹๐–พ๐–บ๐–ฝ๐–บ๐–ป๐—…๐–พ, ๐—†๐–บ๐—‚๐—‡๐—๐–บ๐—‚๐—‡๐–บ๐–ป๐—…๐–พ, ๐–บ๐—‡๐–ฝ ๐—‰๐—‹๐–พ๐–ฝ๐—‚๐–ผ๐—๐–บ๐–ป๐—…๐–พ ๐–ฑ๐–พ๐–บ๐–ผ๐— ๐–ผ๐—ˆ๐—†๐—‰๐—ˆ๐—‡๐–พ๐—‡๐—๐—Œ.

๐–ถ๐—๐–บ๐— ๐–บ๐—‹๐–พ ๐—’๐—ˆ๐—Ž๐—‹ ๐—๐—๐—ˆ๐—Ž๐—€๐—๐—๐—Œ ๐—ˆ๐—‡ ๐—‚๐—†๐—‰๐–พ๐—‹๐–บ๐—๐—‚๐—๐–พ ๐—๐—Œ. ๐–ฝ๐–พ๐–ผ๐—…๐–บ๐—‹๐–บ๐—๐—‚๐—๐–พ ๐—‰๐–บ๐—๐—๐–พ๐—‹๐—‡๐—Œ ๐—‚๐—‡ ๐–ฑ๐–พ๐–บ๐–ผ๐—? ๐–ฒ๐—๐–บ๐—‹๐–พ ๐—’๐—ˆ๐—Ž๐—‹ ๐—‚๐—‡๐—Œ๐—‚๐—€๐—๐—๐—Œ!

React #FrontendDevelopment #JavaScript #WebDevelopment #CleanCode #ReactHooks"

Top comments (0)