国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁(yè) web前端 js教程 存在主義的 React 問題和完美的模態(tài)對(duì)話框

存在主義的 React 問題和完美的模態(tài)對(duì)話框

Jan 03, 2025 am 03:44 AM

Existential React questions and a perfect Modal Dialog

你認(rèn)為React中最復(fù)雜的事情是什么?重新渲染?語(yǔ)境?門戶網(wǎng)站?并發(fā)?

不。

React 最難的部分是它周圍的一切非 React。 “上面列出的那些東西是如何工作的?”這個(gè)問題的答案很簡(jiǎn)單:只需遵循算法并做筆記即可。結(jié)果將是確定的并且始終相同(如果您正確追蹤)。這只是科學(xué)和事實(shí)。

但是“什么讓組件呢?”或“實(shí)施……(某事)的正確方法是什么?”甚至“我應(yīng)該使用庫(kù)還是構(gòu)建自己的解決方案?”這里唯一正確的答案是“這取決于情況”。它恰好是最沒有幫助的一個(gè)。

我想為新文章找到比這更好的東西。但由于這些類型的問題不可能有簡(jiǎn)單的答案和通用的解決方案,因此這篇文章更多地是我的思維過程的演練,而不是“這就是答案,永遠(yuǎn)這樣做”。希望它仍然有用。

那么,如何才能將功能從想法轉(zhuǎn)變?yōu)榭赏度肷a(chǎn)的解決方案呢?讓我們嘗試實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模態(tài)對(duì)話框并看看。那有什么可能是復(fù)雜的呢? ?

第 1 步:從最簡(jiǎn)單的解決方案開始

讓我們從有時(shí)被稱為“尖峰”的東西開始 - 最簡(jiǎn)單的實(shí)現(xiàn),可以幫助探索潛在的解決方案并收集進(jìn)一步的需求。我知道我正在實(shí)現(xiàn)一個(gè)模式對(duì)話框。假設(shè)我有一個(gè)像這樣的漂亮設(shè)計(jì):

Existential React questions and a perfect Modal Dialog

對(duì)話框基本上是屏幕上的一個(gè)元素,當(dāng)單擊按鈕之類的內(nèi)容時(shí)會(huì)出現(xiàn)該元素。這正是我要開始的地方。

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

狀態(tài),一個(gè)監(jiān)聽點(diǎn)擊的按鈕,以及當(dāng)狀態(tài)為 true 時(shí)顯示的未來對(duì)話框。對(duì)話框還應(yīng)該有一個(gè)“關(guān)閉”操作:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

它還有一個(gè)“背景” - 一個(gè)可點(diǎn)擊的半透明 div,覆蓋內(nèi)容并在單擊時(shí)觸發(fā)模式消失。

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>

大家在一起:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}

我通常也會(huì)盡早添加合適的樣式??吹轿艺趯?shí)現(xiàn)的功能以與預(yù)期相同的外觀出現(xiàn)在屏幕上,這有助于我思考。另外,它還可以通知功能的布局,這正是此對(duì)話框?qū)l(fā)生的情況。

讓我們快速為背景添加 CSS - 它沒什么特別的,只是 div 上的半透明背景,位置固定:占據(jù)整個(gè)屏幕:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

該對(duì)話框稍微有趣一些,因?yàn)樗枰胖迷谄聊恢虚g。當(dāng)然,CSS 中有 1001 種方法可以實(shí)現(xiàn)這一目標(biāo),但我最喜歡的也可能是最簡(jiǎn)單的一種是:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

我們使用“固定”位置來擺脫布局約束,添加 50% 的左側(cè)和頂部以將 div 移動(dòng)到中間位置,然后將其變換回 50%。 left 和 top 將相對(duì)于屏幕進(jìn)行計(jì)算,變換將相對(duì)于 div 本身的寬度/高度,因此,無論其寬度或屏幕寬度如何,它都會(huì)出現(xiàn)在中間。

此步驟中 CSS 的最后一點(diǎn)是正確設(shè)置對(duì)話框本身和“關(guān)閉”按鈕的樣式。這里就不復(fù)制粘貼了,實(shí)際的樣式并不重要,看一下例子:

第二步:停下來,提出問題并思考

現(xiàn)在我已經(jīng)粗略地實(shí)現(xiàn)了該功能,是時(shí)候讓它變得“真實(shí)”了。為此,我們需要詳細(xì)了解我們到底要解決什么問題以及為誰解決問題。從技術(shù)上講,我們應(yīng)該明白編碼任何東西之前,所以很多時(shí)候,這一步應(yīng)該是第1步。

此對(duì)話框是否是原型的一部分,需要盡快實(shí)施,向投資者展示一次,然后不再使用?或者它可能是您要在 npm 和開源上發(fā)布的通用庫(kù)的一部分?或者它可能是您的 5,000 人組織將使用的設(shè)計(jì)系統(tǒng)的一部分?或者它只是您的 3 人小型團(tuán)隊(duì)的內(nèi)部工具的一部分,僅此而已?或者,也許您在 TikTok 等公司工作,并且此對(duì)話框?qū)⒊蔀閮H在移動(dòng)設(shè)備上可用的網(wǎng)絡(luò)應(yīng)用程序的一部分?或者您可能在一家只為政府編寫應(yīng)用程序的機(jī)構(gòu)工作?

回答這些問題可以確定下一步編碼的方向。

如果只是一個(gè)原型,用一次,可能就已經(jīng)足夠了。

如果它要作為庫(kù)的一部分開源,它需要有一個(gè)非常好的通用 API,世界上任何開發(fā)人員都可以使用和理解,大量的測(cè)試和良好的文檔。

作為 5,000 人組織的設(shè)計(jì)系統(tǒng)一部分的對(duì)話框需要遵守組織的設(shè)計(jì)準(zhǔn)則,并且可能會(huì)限制將哪些外部依賴項(xiàng)帶入存儲(chǔ)庫(kù)。因此,您可能需要從頭開始實(shí)現(xiàn)許多事情,而不是執(zhí)行 npm install new-fancy-tool。

為政府建立的機(jī)構(gòu)的對(duì)話可能需要成為宇宙中最容易訪問且符合法規(guī)的對(duì)話。否則,該機(jī)構(gòu)可能會(huì)失去政府合同并破產(chǎn)。

等等等等。

出于本文的目的,我們假設(shè)該對(duì)話框是現(xiàn)有大型商業(yè)網(wǎng)站當(dāng)前正在進(jìn)行的全新重新設(shè)計(jì)的一部分,該網(wǎng)站每天有來自世界各地的數(shù)千名用戶。重新設(shè)計(jì)正在進(jìn)行中,我得到的唯一帶有對(duì)話框的設(shè)計(jì)是這樣的:

Existential React questions and a perfect Modal Dialog

剩下的稍后再說,設(shè)計(jì)師們都忙不過來了。此外,我是負(fù)責(zé)重新設(shè)計(jì)和維護(hù)網(wǎng)站的永久團(tuán)隊(duì)的一員,而不是為單個(gè)項(xiàng)目雇用的外部承包商。

在這種情況下,僅憑這張圖片并了解我們公司的目標(biāo)就可以為我提供足夠的信息來做出合理的假設(shè)并實(shí)現(xiàn) 90% 的對(duì)話。剩下的10%可以稍后再微調(diào)。

這些是我根據(jù)上述信息可以做出的假設(shè):

  • 現(xiàn)有網(wǎng)站每天有來自世界各地的數(shù)千名用戶,因此我需要確保該對(duì)話框至少可以在大屏幕和移動(dòng)屏幕以及不同的瀏覽器上運(yùn)行。理想情況下,我需要檢查現(xiàn)有分析才能絕對(duì)確定,但這是一個(gè)非常安全的選擇。

  • 不止一位開發(fā)人員正在為此編寫代碼,并且代碼將保留下來。網(wǎng)站規(guī)模很大,已經(jīng)擁有數(shù)千名用戶;對(duì)于投資者來說,這不是一個(gè)快速的原型。所以,我需要確保代碼可讀,API有意義,可用且可維護(hù),并且沒有明顯的腳槍。

  • 公司關(guān)心其形象和網(wǎng)站的質(zhì)量 - 否則,他們?yōu)槭裁匆M(jìn)行重新設(shè)計(jì)? (我們假設(shè)這里有積極的意圖?)。這意味著需要達(dá)到一定的質(zhì)量水平,我需要提前思考并預(yù)測(cè)常見場(chǎng)景和邊緣情況,即使它們還不是當(dāng)前設(shè)計(jì)的一部分。

  • 許多用戶可能意味著并非所有人都專門使用鼠標(biāo)與網(wǎng)站交互。該對(duì)話框還必須可以通過鍵盤交互甚至屏幕閱讀器等輔助技術(shù)來使用。

  • 現(xiàn)有的大型代碼庫(kù)(記住,這是重新設(shè)計(jì)?。┮馕吨铱梢詾榇斯δ軒淼耐獠恳蕾図?xiàng)可能存在限制。任何外部依賴都是有代價(jià)的,尤其是在大型和舊的代碼庫(kù)中。出于本文的目的,我們假設(shè)我可以使用外部庫(kù),但我需要對(duì)此有一個(gè)很好的理由。

  • 最后,更多的設(shè)計(jì)即將到來,所以我需要從設(shè)計(jì)和用戶的角度預(yù)測(cè)它會(huì)走向何方,并確保代碼可以盡早處理它。

第 3 步:固化模態(tài)對(duì)話框 API

現(xiàn)在我知道了需求并有了合理的猜測(cè),我可以制作實(shí)際的對(duì)話框組件了。首先,從這段代碼來看:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

我絕對(duì)需要將對(duì)話框部分提取到可重用組件中 - 將有大量基于對(duì)話框的功能需要實(shí)現(xiàn)。

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

對(duì)話框?qū)⒂幸粋€(gè) onClose 屬性 - 當(dāng)單擊“關(guān)閉”按鈕或背景時(shí),它將通知父組件。然后,父組件仍將具有狀態(tài)并呈現(xiàn)對(duì)話框,如下所示:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>

現(xiàn)在,讓我們?cè)俅慰纯丛O(shè)計(jì)并更多地考慮對(duì)話框:

Existential React questions and a perfect Modal Dialog

對(duì)話框中顯然會(huì)有一些帶有操作按鈕的“頁(yè)腳”部分。這些按鈕很可能會(huì)有很多變化 - 一個(gè)、兩個(gè)、三個(gè)、左對(duì)齊、右對(duì)齊、中間有空格等等。此外,此對(duì)話框沒有 標(biāo)題 ,但是它非常非常有可能具有某些標(biāo)題的對(duì)話框是一種非常常見的模式。這里絕對(duì)會(huì)有一個(gè)內(nèi)容區(qū)域,其中包含完全隨機(jī)的內(nèi)容 - 從確認(rèn)文本到表格,再到互動(dòng)體驗(yàn),再到?jīng)]有人閱讀的很長(zhǎng)的“條款和條件”可滾動(dòng)文本。

最后是尺寸。設(shè)計(jì)中的對(duì)話框很小,只是一個(gè)確認(rèn)對(duì)話框。大表格或長(zhǎng)文本不適合那里。因此,考慮到我們?cè)诓襟E 2 中收集的信息,可以非常安全地假設(shè)對(duì)話框的大小需要更改。此時(shí),考慮到設(shè)計(jì)師可能有設(shè)計(jì)指南,我們可以假設(shè)對(duì)話框有三種變體:“小”、“中”和“大”。

所有這些意味著我們需要在 ModalDialog 上有 props:頁(yè)腳和 header 將只是接受 ReactNode 的常規(guī) props,大小將只是字符串的聯(lián)合,而內(nèi)容區(qū)域作為主要部分將進(jìn)入孩子們:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}

我們將使用來自道具的附加 className 來控制對(duì)話框的大小。但在現(xiàn)實(shí)生活中,它將很大程度上取決于存儲(chǔ)庫(kù)中使用的樣式解決方案。

然而,在這個(gè)變體中,對(duì)話框太靈活了 - 幾乎任何東西都可以去任何地方。例如,在頁(yè)腳中,大多數(shù)時(shí)候,我們只需要一兩個(gè)按鈕,僅此而已。這些按鈕必須一致地排列在整個(gè)網(wǎng)站的各處。我們需要一個(gè)包裝器來對(duì)齊它們:

.backdrop {
  background: rgba(0, 0, 0, 0.3);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

與內(nèi)容相同 - 至少,它需要一些周圍的填充和滾動(dòng)能力。標(biāo)題可能需要一些文本樣式。于是布局就變成了這樣:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

但不幸的是,我們無法保證這一點(diǎn)。在某些時(shí)候,很可能有人希望在頁(yè)腳中添加除按鈕之外的更多內(nèi)容。或者某些對(duì)話框需要在已售背景上有標(biāo)題?;蛘哂袝r(shí),內(nèi)容不需要填充。

我在這里要指出的是,有一天我們需要能夠設(shè)計(jì)頁(yè)眉/內(nèi)容/頁(yè)腳部分的樣式。而且可能比預(yù)期要早。

當(dāng)然,我們可以只使用 props 傳遞該配置,并使用 headerClassName、contentClassName 和 footerClassName 等 props。實(shí)際上,對(duì)于某些情況來說,這可能沒問題。但對(duì)于像重新設(shè)計(jì)的漂亮對(duì)話框這樣的東西,我們可以做得更好。

解決這個(gè)問題的一個(gè)非常巧妙的方法是將我們的頁(yè)眉/內(nèi)容/頁(yè)腳提取到它們自己的組件中,如下所示:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

并將 ModalDialog 代碼恢復(fù)為沒有包裝器的代碼:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>

這樣,在父應(yīng)用程序中,如果我想要對(duì)話框部分的默認(rèn)設(shè)計(jì),我會(huì)使用這些微小的組件:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}

如果我想要完全自定義的東西,我會(huì)實(shí)現(xiàn)一個(gè)具有自己的自定義樣式的新組件,而不會(huì)弄亂 ModalDialog 本身:

.backdrop {
  background: rgba(0, 0, 0, 0.3);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

就此而言,我什至不再需要頁(yè)眉和頁(yè)腳道具。我可以將 DialogHeader 和 DialogFooter 傳遞給子級(jí),進(jìn)一步簡(jiǎn)化 ModalDialog,并擁有更好的 API,具有相同級(jí)別的靈活性,同時(shí)在各處都具有一致的設(shè)計(jì)。

父組件將如下所示:

.dialog {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

對(duì)話框的 API 將如下所示:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}

到目前為止我對(duì)此非常滿意。它足夠靈活,可以以設(shè)計(jì)可能需要的任何方式進(jìn)行擴(kuò)展,但它也足夠清晰和合理,可以輕松地在整個(gè)應(yīng)用程序中實(shí)現(xiàn)一致的 UI。

這是可以使用的實(shí)例:

第四步:性能和重新渲染

現(xiàn)在 Modal 的 API 已經(jīng)足夠好了,是時(shí)候解決我實(shí)現(xiàn)的明顯的腳槍問題了。如果你讀夠了我的文章,你可能已經(jīng)大聲尖叫,“你在做什么???重新渲染??!”最后十分鐘?當(dāng)然,你是對(duì)的:

const ModalDialog = ({ onClose }) => {
  return (
    <>
      <div className="backdrop" onClick={onClose}></div>
      <div className="dialog">
        <button className="close-button" onClick={onClose}>
          Close
        </button>
      </div>
    </>
  );
};

這里的Page組件是有狀態(tài)的。每次模式打開或關(guān)閉時(shí),狀態(tài)都會(huì)發(fā)生變化,并且會(huì)導(dǎo)致整個(gè)組件及其內(nèi)部所有內(nèi)容的重新渲染。是的,“過早的優(yōu)化是萬惡之源”,是的,在實(shí)際測(cè)量性能之前不要優(yōu)化性能,在這種情況下,我們可以安全地忽略傳統(tǒng)觀點(diǎn)。

有兩個(gè)原因。首先,我知道一個(gè)事實(shí)是,整個(gè)應(yīng)用程序中會(huì)分散很多模式。這不是一個(gè)沒有人會(huì)使用的一次性隱藏功能。因此,有人將狀態(tài)放置在不應(yīng)該使用這樣的 API 的地方的可能性非常高。其次,從一開始就不需要花費(fèi)太多時(shí)間和精力來防止重新渲染問題的發(fā)生。只要1分鐘的努力,我們根本不需要考慮這里的性能。

我們需要做的就是封裝狀態(tài)并引入“不受控組件”的想法:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

BaseModalDialog 與我們之前的對(duì)話框完全相同,我只是重命名了它。

然后傳遞一個(gè)應(yīng)該觸發(fā)對(duì)話框的組件作為觸發(fā)道具:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

頁(yè)面組件將如下所示:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>

頁(yè)面內(nèi)不再有狀態(tài),不再有潛在危險(xiǎn)的重新渲染。

這樣的 API 應(yīng)該涵蓋 95% 的用例,因?yàn)榇蠖鄶?shù)時(shí)候,用戶需要單擊某些內(nèi)容才能顯示對(duì)話框。在極少數(shù)情況下,當(dāng)對(duì)話框需要獨(dú)立顯示時(shí),例如,在快捷方式上或作為入門的一部分,我仍然可以使用 BaseModalDialog 并手動(dòng)處理狀態(tài)。

第 5 步:處理邊緣情況和可訪問性

從 React 的角度來看,ModalDialog 組件的 API 非??煽?,但工作還遠(yuǎn)未完成??紤]到我在第 2 步中收集的必備條件,我還需要解決更多問題。

問題 1:我將觸發(fā)器包裝到一個(gè)額外的跨度中 - 在某些情況下,這可能會(huì)破壞頁(yè)面的布局。我需要以某種方式去掉包裝紙。

問題 2:如果我在創(chuàng)建新堆疊上下文的元素內(nèi)渲染對(duì)話框,則模式將出現(xiàn)在某些元素下方。我需要在 Portal 內(nèi)渲染它,而不是像我現(xiàn)在一樣直接在布局內(nèi)渲染。

問題 3:目前鍵盤訪問非常糟糕。當(dāng)正確實(shí)現(xiàn)的模式對(duì)話框打開時(shí),焦點(diǎn)應(yīng)該跳到里面。當(dāng)它關(guān)閉時(shí) - 焦點(diǎn)應(yīng)該返回到觸發(fā)對(duì)話框的元素。當(dāng)對(duì)話框打開時(shí),焦點(diǎn)應(yīng)該被“困”在里面,而外面的元素不應(yīng)該是可聚焦的。按 ESC 按鈕應(yīng)關(guān)閉該對(duì)話框。目前這些都還沒有實(shí)現(xiàn)。

問題 1 和 2 有點(diǎn)煩人,但可以相對(duì)快速地解決。然而,手動(dòng)完成第 3 個(gè)問題是一件非常痛苦的事情。另外,這肯定是一個(gè)已解決的問題 - 每個(gè)地方的每個(gè)對(duì)話框都需要此功能。

“我自己做的巨大痛苦”“看起來肯定是一個(gè)已解決的問題”的組合是我尋找現(xiàn)有庫(kù)的地方。

考慮到我已經(jīng)完成的所有前期工作,現(xiàn)在選擇合適的就很容易了。

我可以使用任何現(xiàn)有的 UI 組件庫(kù),例如 Ant Design 或 Material UI,并使用其中的對(duì)話框。但如果重新設(shè)計(jì)不使用它們,將他們的設(shè)計(jì)調(diào)整為我需要的,會(huì)帶來比他們解決的更多的痛苦。所以對(duì)于這種情況,立即否定。

我可以使用“無頭”UI 庫(kù)之一,例如 Radix 或 React Aria。它們實(shí)現(xiàn)了狀態(tài)和觸發(fā)器等功能以及所有可訪問性,但將設(shè)計(jì)留給了消費(fèi)者。在查看他們的 API 時(shí),我需要仔細(xì)檢查它們是否允許我控制對(duì)話框的狀態(tài),如果我確實(shí)需要它來手動(dòng)觸發(fā)對(duì)話框(他們確實(shí)這樣做)。

如果由于某種原因,我無法使用無頭庫(kù),我至少會(huì)嘗試使用處理焦點(diǎn)陷阱功能的庫(kù)。

為了本文的目的,我們假設(shè)我可以帶任何我想要的庫(kù)。在這種情況下,我將使用 Radix - 它非常易于使用,并且對(duì)話框的 API 看起來與我已經(jīng)實(shí)現(xiàn)的非常相似,因此重構(gòu)應(yīng)該是輕而易舉的。

我們需要稍微更改一下對(duì)話框本身的 API:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}

和我以前的幾乎一樣。只是,我沒有使用 div,而是使用 Radix 原語(yǔ)。

不受控制的對(duì)話框用法根本沒有改變:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>

受控對(duì)話框略有變化 - 我需要將道具傳遞給它而不是條件渲染:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>

查看下面的示例并嘗試使用鍵盤進(jìn)行導(dǎo)航。一切都按照我的需要進(jìn)行,這有多酷?

作為獎(jiǎng)勵(lì),Radix 還可以處理 Portal 問題,并且它不會(huì)將觸發(fā)器包裝在一個(gè)跨度中。我不再需要解決邊緣情況,所以我可以繼續(xù)最后一步。

第6步:最后拋光

該功能還沒有完成! ?該對(duì)話框現(xiàn)在看起來和感覺都相當(dāng)可靠,因此現(xiàn)階段我不會(huì)對(duì)其實(shí)現(xiàn)進(jìn)行任何重大更改。但對(duì)于我正在解決的用例,它仍然需要一些東西才能被認(rèn)為是“完美”對(duì)話框。

One:設(shè)計(jì)師要求我做的第一件事(如果他們還沒有做的話)就是在對(duì)話框打開時(shí)添加一個(gè)微妙的動(dòng)畫。需要預(yù)見它并記住如何在 React 中制作動(dòng)畫。

兩個(gè):我需要向?qū)υ捒蛱砑幼畲髮挾群妥畲蟾叨?,以便在小屏幕上它仍然看起來不錯(cuò)。想想它在大屏幕上的樣子。

:我需要與設(shè)計(jì)師討論對(duì)話框在移動(dòng)設(shè)備上的行為方式。他們很可能會(huì)要求我將其做成一個(gè)滑入式面板,無論對(duì)話框的大小如何,它都會(huì)占據(jù)大部分屏幕。

四個(gè):我需要至少引入 DialogTitle 和 DialogDescription 組件 - Radix 會(huì)要求將它們用于輔助功能。

:測(cè)試!該對(duì)話框?qū)⒈A粝聛聿⒂善渌司S護(hù),因此在這種情況下測(cè)試幾乎是強(qiáng)制性的。

也許還有很多我現(xiàn)在忘記的小事情,稍后會(huì)出現(xiàn)。更不用說實(shí)現(xiàn)對(duì)話框內(nèi)容的實(shí)際設(shè)計(jì)了。

還有一些想法

如果將上面的“對(duì)話框”替換為“SomeNewFeature”,這或多或少是我用來實(shí)現(xiàn)幾乎所有新功能的算法。

解決方案的快速“峰值”→收集功能需求→使其工作→使其高性能→使其完整→使其完美。

對(duì)于像實(shí)際對(duì)話框這樣的東西,我已經(jīng)實(shí)現(xiàn)了數(shù)百次,我會(huì)在 10 秒內(nèi)在腦海中完成第一步,然后立即從步驟 2 開始。

對(duì)于非常復(fù)雜和未知的事情,第 1 步可能會(huì)更長(zhǎng),并且涉及立即探索不同的解決方案和庫(kù)。

一些不完全未知的東西,只是“我們需要做的常規(guī)功能”,可能會(huì)跳過步驟 1,因?yàn)榭赡軟]有什么可探索的。

很多時(shí)候,尤其是在“敏捷”環(huán)境中,它更像是螺旋而不是直線,需求是增量提供的并且經(jīng)常變化,我們會(huì)定期返回前兩個(gè)步驟。


希望此類文章有用! ??如果您想要更多這樣的內(nèi)容或者更喜歡通常的“事情如何運(yùn)作”的內(nèi)容,請(qǐng)告訴我。

并期待聽到你們所有人的頭腦中這個(gè)過程有何不同?


最初發(fā)布于 https://www.developerway.com。網(wǎng)站還有更多這樣的文章嗎?

看看《Advanced React》一書,將您的 React 知識(shí)提升到一個(gè)新的水平。

訂閱時(shí)事通訊、在 LinkedIn 上聯(lián)系或在 Twitter 上關(guān)注,以便在下一篇文章發(fā)布時(shí)立即收到通知。


順便說一句,最后一件事:如果您很快就要開始一個(gè)新項(xiàng)目,并且沒有設(shè)計(jì)師,也沒有時(shí)間來完善所描述的設(shè)計(jì)體驗(yàn) - 我最近花了幾個(gè)小時(shí)(又幾個(gè)小時(shí))來實(shí)現(xiàn)一個(gè)新的項(xiàng)目本例的 UI 組件庫(kù)。它具有可復(fù)制粘貼的組件和常見模式、Radix 和 Tailwind、深色模式、可訪問性以及開箱即用的移動(dòng)支持。包括上面完美的模態(tài)對(duì)話框! ?

嘗試一下:https://www.buckets-ui.com/

Existential React questions and a perfect Modal Dialog

以上是存在主義的 React 問題和完美的模態(tài)對(duì)話框的詳細(xì)內(nèi)容。更多信息請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣服圖片

Undresser.AI Undress

Undresser.AI Undress

人工智能驅(qū)動(dòng)的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用于從照片中去除衣服的在線人工智能工具。

Clothoff.io

Clothoff.io

AI脫衣機(jī)

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的代碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

功能強(qiáng)大的PHP集成開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網(wǎng)頁(yè)開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)代碼編輯軟件(SublimeText3)

Java vs. JavaScript:清除混亂 Java vs. JavaScript:清除混亂 Jun 20, 2025 am 12:27 AM

Java和JavaScript是不同的編程語(yǔ)言,各自適用于不同的應(yīng)用場(chǎng)景。Java用于大型企業(yè)和移動(dòng)應(yīng)用開發(fā),而JavaScript主要用于網(wǎng)頁(yè)開發(fā)。

JavaScript評(píng)論:簡(jiǎn)短說明 JavaScript評(píng)論:簡(jiǎn)短說明 Jun 19, 2025 am 12:40 AM

JavascriptconcommentsenceenceEncorenceEnterential gransimenting,reading and guidingCodeeXecution.1)單inecommentsareusedforquickexplanations.2)多l(xiāng)inecommentsexplaincomplexlogicorprovideDocumentation.3)

如何在JS中與日期和時(shí)間合作? 如何在JS中與日期和時(shí)間合作? Jul 01, 2025 am 01:27 AM

JavaScript中的日期和時(shí)間處理需注意以下幾點(diǎn):1.創(chuàng)建Date對(duì)象有多種方式,推薦使用ISO格式字符串以保證兼容性;2.獲取和設(shè)置時(shí)間信息可用get和set方法,注意月份從0開始;3.手動(dòng)格式化日期需拼接字符串,也可使用第三方庫(kù);4.處理時(shí)區(qū)問題建議使用支持時(shí)區(qū)的庫(kù),如Luxon。掌握這些要點(diǎn)能有效避免常見錯(cuò)誤。

為什么要將標(biāo)簽放在的底部? 為什么要將標(biāo)簽放在的底部? Jul 02, 2025 am 01:22 AM

PlacingtagsatthebottomofablogpostorwebpageservespracticalpurposesforSEO,userexperience,anddesign.1.IthelpswithSEObyallowingsearchenginestoaccesskeyword-relevanttagswithoutclutteringthemaincontent.2.Itimprovesuserexperiencebykeepingthefocusonthearticl

JavaScript與Java:開發(fā)人員的全面比較 JavaScript與Java:開發(fā)人員的全面比較 Jun 20, 2025 am 12:21 AM

JavaScriptIspreferredforredforwebdevelverment,而Javaisbetterforlarge-ScalebackendsystystemsandSandAndRoidApps.1)JavascriptexcelcelsincreatingInteractiveWebexperienceswebexperienceswithitswithitsdynamicnnamicnnamicnnamicnnamicnemicnemicnemicnemicnemicnemicnemicnemicnddommanipulation.2)

JavaScript:探索用于高效編碼的數(shù)據(jù)類型 JavaScript:探索用于高效編碼的數(shù)據(jù)類型 Jun 20, 2025 am 12:46 AM

javascripthassevenfundaMentalDatatypes:數(shù)字,弦,布爾值,未定義,null,object和symbol.1)numberSeadUble-eaduble-ecisionFormat,forwidevaluerangesbutbecautious.2)

什么是在DOM中冒泡和捕獲的事件? 什么是在DOM中冒泡和捕獲的事件? Jul 02, 2025 am 01:19 AM

事件捕獲和冒泡是DOM中事件傳播的兩個(gè)階段,捕獲是從頂層向下到目標(biāo)元素,冒泡是從目標(biāo)元素向上傳播到頂層。1.事件捕獲通過addEventListener的useCapture參數(shù)設(shè)為true實(shí)現(xiàn);2.事件冒泡是默認(rèn)行為,useCapture設(shè)為false或省略;3.可使用event.stopPropagation()阻止事件傳播;4.冒泡支持事件委托,提高動(dòng)態(tài)內(nèi)容處理效率;5.捕獲可用于提前攔截事件,如日志記錄或錯(cuò)誤處理。了解這兩個(gè)階段有助于精確控制JavaScript響應(yīng)用戶操作的時(shí)機(jī)和方式。

如何減少JavaScript應(yīng)用程序的有效載荷大小? 如何減少JavaScript應(yīng)用程序的有效載荷大??? Jun 26, 2025 am 12:54 AM

如果JavaScript應(yīng)用加載慢、性能差,問題往往出在payload太大,解決方法包括:1.使用代碼拆分(CodeSplitting),通過React.lazy()或構(gòu)建工具將大bundle拆分為多個(gè)小文件,按需加載以減少首次下載量;2.移除未使用的代碼(TreeShaking),利用ES6模塊機(jī)制清除“死代碼”,確保引入的庫(kù)支持該特性;3.壓縮和合并資源文件,啟用Gzip/Brotli和Terser壓縮JS,合理合并文件并優(yōu)化靜態(tài)資源;4.替換重型依賴,選用輕量級(jí)庫(kù)如day.js、fetch

See all articles