欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React彈窗使用方式NiceModal重新思考

 更新時間:2022年08月12日 10:49:38   作者:海秋  
這篇文章主要為大家介紹了React彈窗使用方式NiceModal重新思考分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一些有趣的真實場景

在開始進(jìn)入正題之前,先看看一些與彈窗有關(guān)的有趣場景 ??。

案例一:全局彈窗

上圖是掘金的登錄彈窗,未登錄狀態(tài)下觸發(fā)該彈窗展示的時機有很多,比如:

  • 點擊 Header 上的登錄按鈕
  • 文章列表頁或詳情頁點贊以及評論文章
  • 發(fā)沸點、評論沸點以及點贊沸點
  • ...

開發(fā)者往往會基于第三方組件庫定義一個 <LoginModal />,然后將其掛載至 Root 組件下:

function App() {
  const [visible, setVisible] = useState(false);
  const [otherLoginData, setOtherLoginData] = useState();
  // other logic ...
  return (
    <div className="app">
      <Main />
      <LoginModal
        visible={visible}
        onCancel={() => setVisible(false)}
        otherLoginData={otherLoginData}
      />
    </div>
  );
}

這樣會帶來一些問題:

  • <LoginModal /> 內(nèi)部邏輯在組件渲染的時候就會執(zhí)行,即使彈窗處于隱藏狀態(tài)
  • 額外的復(fù)雜度。由于存在多個觸發(fā)時機,需要將 setVisible 以及 setOtherLoginData 透傳至 <Main /> 內(nèi)部的多個子組件,你可以選擇通過 props 一層一層的傳遞進(jìn)去(鬼知道有多少層?。?,也可以引入工具進(jìn)行狀態(tài)共享,但不論怎樣,這都不是一件容易的事;
  • 隨著類似的彈窗越來越多,根組件會維護很多彈窗組件的狀態(tài)...天知道為什么展示一個彈窗需要在多個文件里反復(fù)橫跳。

展示一個彈窗,為什么會變得如此復(fù)雜?

以上案例來自 @ebay/nice-modal-react,稍作修改

除了上述全局彈窗的場景,還有一種場景也很讓人頭疼。

案例二:存在分支以及依賴關(guān)系的彈窗

彈窗 1 確認(rèn)彈出彈窗 2,取消則彈出彈窗 3,彈窗 2 以及 彈窗 3 也存在對應(yīng)的分支邏輯,子孫滿堂輕而易舉,若按照常規(guī)聲明式彈窗組件的實現(xiàn),非常恐怖!

不那么通用的解決方案

擺脫所謂的 React 哲學(xué):數(shù)據(jù)驅(qū)動視圖(view = f(data)),回歸原始的 window.confirm 以及 window.alert,很多問題迎刃而解:

let mountNode: HTMLDivElement | null = null;
LoginModal.show = (props?: LoginModalProps) => {
  if (!mountNode) {
    mountNode = document.createElement('div');
    document.body.appendChild(mountNode);
  }
  // 通過 ReactDOM.render 將組件渲染到固定的節(jié)點
  ReactDOM.render(<LoginModal {...props} visible />, mountNode);
};
LoginModal.hide = () => {
  mountNode && ReactDOM.render(<LoginModal {...props} visible={false} />, mountNode);
};
LoginModal.destroy = () => {
  // 通過 ReactDOM.unmountComponentAtNode 卸載節(jié)點
  mountNode && ReactDOM.unmountComponentAtNode(mountNode);
};

經(jīng)過以上代碼處理,可以直接通過 LoginModal.show({otherLoginData}) 展示彈窗(hide 以及 destroy 同理)。

對于存在依賴關(guān)系的彈窗,則可以通過返回 Promise 進(jìn)行鏈?zhǔn)秸{(diào)用,使用方式如下,此處不過多展開。

try {
  const ret = await LoginModal.show({ otherLoginData });
  const ret = await Ohter1Modal.show();
} catch (error) {
  const ret = await Other2Modal.show();
}

由于該方案是通過 ReactDOM.render 將組件渲染到一個新節(jié)點,脫離了原始的 React 上下文,在某些強依賴 Context 的場景會顯得有些麻煩。

可能是最好的彈窗實踐

某天在逛 github 的時候發(fā)現(xiàn)了 @ebay/nice-modal-react ,讓我們看看 ebay 的開發(fā)者是如何讓彈窗在 React 下變得足夠 Nice。

創(chuàng)建彈窗

import { Modal } from 'antd';
import NiceModal, { useModal } from '@ebay/nice-modal-react';
export default NiceModal.create(({ name }) => {
  // Use a hook to manage the modal state
  const modal = useModal();
  return (
    <Modal
      title="Hello Antd"
      onOk={() => modal.hide()}
      visible={modal.visible}
      onCancel={() => modal.hide()}
      afterClose={() => modal.remove()}
    >
      Hello {name}!
    </Modal>
  );
});

和原先基于 antd 自定義一個彈窗組件非常相似,只不過彈窗顯隱相關(guān)的 props(如 visible/hide/remove) 是通過 useModal 獲取,最外層則通過 NiceModal.create 將組件進(jìn)行一層封裝(高階組件)。

使用彈窗

在使用彈窗之前,需要在Root 節(jié)點引入 <NiceModal.Provider />

import NiceModal from '@ebay/nice-modal-react';
ReactDOM.render(
  <React.StrictMode>
    <NiceModal.Provider>
      <App />
    </NiceModal.Provider>
  </React.StrictMode>,
  document.getElementById('root'),
);

隨后便可在任意地方通過 NiceModal.show 展示先前自定義的彈窗:

import NiceModal from '@ebay/nice-modal-react';
import MyAntdModal from './my-antd-modal'; // created by above code
function App() {
  const showAntdModal = () => {
    // Show a modal with arguments passed to the component as props
    NiceModal.show(MyAntdModal, { name: 'Nate' })
  };
  return (
    <div className="app">
      <h1>Nice Modal Examples</h1>
      <div className="demo-buttons">
        <button onClick={showAntdModal}>Antd Modal</button>
      </div>
    </div>
  );
}

以上指引參考自 NiceModal 官方文檔

經(jīng)過 NiceModal 的封裝,好處顯而易見:

  • 調(diào)用過程干凈優(yōu)雅
  • 組件依舊存在于上下文中(可以自定義位置,默認(rèn)在 Provider 下)
  • show/hide 方法返回值為 Promise,方便鏈?zhǔn)秸{(diào)用(通過useModal().resolve(val)等方法改變 Promise 狀態(tài))

簡單實現(xiàn)思路

如果你對 NiceModal 的實現(xiàn)原理有興趣,那么可以思考一下我們之前的痛點是什么?案例一中描述了兩大主要痛點:

  • 根組件渲染彈窗以及維護彈窗狀態(tài)過多 —— NiceModal.Provider 內(nèi)統(tǒng)一渲染彈窗
  • 顯隱相關(guān)狀態(tài)以及方法需要共享 —— 庫內(nèi)部維護對應(yīng)狀態(tài),通過統(tǒng)一 API (show/hide/useModal)暴露

案例二的鏈?zhǔn)秸{(diào)用在脫離聲明式組件后反而變得很容易,利用好 Promise 即可。

const Provider: React.FC = ({ children }) => {
  // 初始彈窗信息
  const [modals, _dispatch] = useReducer(reducer, initialState);
  dispatch = _dispatch;
  return (
    <NiceModalContext.Provider value={modals}>
      {children}
      <NiceModalPlaceholder />
    </NiceModalContext.Provider>
  );
};
const NiceModalPlaceholder: React.FC = () => {
  const modals = useContext(NiceModalContext);
  // 根據(jù)彈窗信息找到需要渲染的彈窗 ID (NiceModal.show 時更新)
  const visibleModalIds = Object.keys(modals).filter((id) =>
    Boolean(modals[id])
  );
  // 找到對應(yīng)創(chuàng)建的高階組件NiceModal.show 時注冊),注入 ID
  const toRender = visibleModalIds
    .filter((id) => MODAL_REGISTRY[id])
    .map((id) => ({
      id,
      ...MODAL_REGISTRY[id],
    }));
  return (
    <>
      {toRender.map((t) => (
        {/* 渲染 NiceModal.create 創(chuàng)建的高階組件 */}
        <t.comp key={t.id} id={t.id} />
      ))}
    </>
  );
};
const create =
  <P extends Record<string, unknown>>(
    Comp: React.ComponentType<P>
  ): React.FC<P & NiceModalHocProps> =>
  ({ id }) => {
    const modals = useContext(NiceModalContext);
    const shouldMount = Boolean(modals[id]);
    if (!shouldMount) {
      return null;
    }
    return (
      <NiceModalIdContext.Provider value={id}>
        {/* 找到對應(yīng) ID 的參數(shù),傳入內(nèi)部真實組件 */}
        <Comp {...(modals[id].args as P)} />
      </NiceModalIdContext.Provider>
    );
  };

理解上述三個方法后,show/hide/useModal 等方法就是基于 dispatch 函數(shù)進(jìn)行彈窗信息 Context 的更新從而觸發(fā)視圖更新(其中 show 方法多一步注冊,生成 ID 并綁定至對應(yīng)的高階組件)。

推薦閱讀

以上就是React彈窗使用方式NiceModal重新思考的詳細(xì)內(nèi)容,更多關(guān)于React彈窗使用方式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React Hook用法示例詳解(6個常見hook)

    React Hook用法示例詳解(6個常見hook)

    這篇文章主要介紹了React Hook用法詳解(6個常見hook),本文通過實例代碼給大家介紹了6個常見hook,需要的朋友可以參考下
    2021-04-04
  • React onClick/onChange傳參(bind綁定)問題

    React onClick/onChange傳參(bind綁定)問題

    這篇文章主要介紹了React onClick/onChange傳參(bind綁定)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • React+Koa實現(xiàn)文件上傳的示例

    React+Koa實現(xiàn)文件上傳的示例

    這篇文章主要介紹了React+Koa實現(xiàn)文件上傳的示例,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • React?Hooks的useState、useRef使用小結(jié)

    React?Hooks的useState、useRef使用小結(jié)

    React Hooks 是 React 16.8 版本引入的新特性,useState和useRef是兩個常用的Hooks,本文主要介紹了React?Hooks的useState、useRef使用,感興趣的可以了解一下
    2024-01-01
  • react導(dǎo)出excel文件的四種方式

    react導(dǎo)出excel文件的四種方式

    本文主要介紹了react導(dǎo)出excel文件的四種方式,主要包括原生js導(dǎo)出,使用?js-export-excel,使用xlsx導(dǎo)出, 使用react-html-table-to-excel,感興趣的可以了解一下
    2023-11-11
  • React中的axios模塊及使用方法

    React中的axios模塊及使用方法

    axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中,本文給大家分享React中axios模塊的使用方法,感興趣的朋友一起看看吧
    2022-03-03
  • react dva實現(xiàn)的代碼

    react dva實現(xiàn)的代碼

    dva是一個基于redux和redux-saga的數(shù)據(jù)流方案,然后為了簡化開發(fā)體驗,dva額外內(nèi)置了react-router,fetch,可以激烈為一個輕量級的應(yīng)用框架,這篇文章主要介紹了react dva實現(xiàn),需要的朋友可以參考下
    2021-11-11
  • React快速入門教程

    React快速入門教程

    本文主要介紹了React的相關(guān)知識,具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • 解決React報錯No duplicate props allowed

    解決React報錯No duplicate props allowed

    這篇文章主要為大家介紹了React報錯No duplicate props allowed解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React服務(wù)端渲染(總結(jié))

    React服務(wù)端渲染(總結(jié))

    當(dāng)我們要求渲染時間盡量快、頁面響應(yīng)速度快時就會用到服務(wù)端渲染,本篇文章主要介紹了React服務(wù)端渲染,有興趣的可以了解一下
    2017-07-07

最新評論