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)