איך לכתוב לבד דיאלוג מודאלי בריאקט ב 2023

26/10/2023

בוב דילן שר על הזמנים שמשתנים ואיתם גם היכולות המובנות בדפדפנים. הרבה זמן אחד הדברים שהיה חסר למתכנתים ולמתכנתות והיה אחת הסיבות לשילוב ספריה חיצונית ל UI באפליקציית ריאקט, היה דיאלוג מודאלי. היום עם תמיכה מובנית בדפדפנים זו כבר לא בעיה והאתגר היחיד הוא להתאים את ממשק הפקודות של הדפדפן לאופי הדקלרטיבי של ריאקט. בואו נראה איך זה עובד.

1. פיתוח דיאלוג מודאלי בריאקט בגישה אימפרטיבית

האלמנט dialog של HTML יכול להיפתח בצורה מודאלית בעזרת הפונקציה showModal של JavaScript. לכן הדרך הכי פשוטה להציג דיאלוג מודאלי בריאקט היא ליצור אלמנט כזה, לשמור אותו ב ref ולקרוא לפונקציה כשרוצים להציג או להסתיר אותו. הנה הקוד:

import { useRef } from "react";

export default function App() {
  const modal = useRef(null);
  const show = () => {
    modal.current.showModal();
  };

  const hide = () => {
    modal.current.close();
  };

  return (
    <div className="App">
      <p>
        Hello World
        <button onClick={show}>show dialog</button>
      </p>
      <dialog ref={modal}>
        <p>Dialog Content</p>
        <button onClick={hide}>Close</button>
      </dialog>{" "}
    </div>
  );
}

2. הפיכת המנגנון לדקלרטיבי

הבעיה שמתכנתי ריאקט לא אוהבים להגדיר מה קורה כשלוחצים על כפתור. אנחנו מעדיפים להסתכל על היישום, לדבר על הסטייט שלו ולחשוב איך הממשק נראה כשמשתני הסטייט מכילים ערכים מסוימים. הקוד למעלה אומנם מציג ומסתיר את הדיאלוג, אבל לא ברור איך לסנכרן את הדיאלוג המודאלי הזה עם חלקים אחרים ביישום.

הדרך המרכזית לחבר בין קוד ריאקטי לקוד דפדפן שאינו ריאקטי היא הפקודה useEffect. אלה הצעדים שניקח בשביל להפוך את הקוד לדקלרטיבי:

  1. נוציא את הדיאלוג לקומפוננטה משלו, שתציג בתוך הדיאלוג את הילדים שלה.

  2. נשתמש ב Prop של הקומפוננטה כדי להחליט אם צריך להציג או להסתיר את הדיאלוג.

  3. נעביר ב Prop פונקציה שתיקרא כשמשתמש סוגר את הדיאלוג.

  4. נפעיל את הקומפוננטה מבחוץ ונשמור סטייט שיקבע אם הדיאלוג יוצג או יוסתר.

קוד? ברור:

import { useRef, useState, useEffect } from "react";

function Modal({ children, visible, onClose }) {
  const modal = useRef(null);

  const show = () => {
    modal.current.showModal();
  };

  const hide = () => {
    modal.current.close();
  };

  useEffect(() => {
    if (visible) {
      show();
    } else {
      hide();
    }
  }, [visible]);

  return (
    <dialog ref={modal}>
      {children}
      <button onClick={onClose}>Close</button>
    </dialog>
  );
}

export default function App() {
  const [showModal, setShowModal] = useState(false);

  return (
    <div className="App">
      <p>
        Hello World
        <button onClick={() => setShowModal(true)}>show dialog</button>
        <p>{JSON.stringify(showModal)}</p>
        <p>
          <label>
            <input
              type="checkbox"
              checked={showModal}
              onChange={(e) => setShowModal(e.target.value)}
            />
            Show Modal
          </label>
        </p>
        <Modal visible={showModal} onClose={() => setShowModal(false)}>
          <p>Hello world</p>
        </Modal>
      </p>
    </div>
  );
}

ולייב בקודסנדבוקס למיטיבי לכת: https://codesandbox.io/s/busy-bassi-rfpnyt?file=/src/App.js:0-1135