網(wǎng)站首頁 編程語言 正文
React中使用對話框并不容易,主要因為:
- 對話框需要在父組件中聲明,才能在子組件中控制其是否顯示
- 給對話框傳遞參數(shù)只能由props傳入,這意味著所有狀態(tài)管理都必須在更高階的組件中。而實際上這個對話框的參數(shù)只在子組件中才會維護(hù)。這時就需要我們使用自定義事件將參數(shù)傳回
這些問題的本質(zhì)就是:如何用一個統(tǒng)一的方式去管理對話框,從而讓對話框相關(guān)的業(yè)務(wù)邏輯更加模塊化,以及和其他業(yè)務(wù)邏輯進(jìn)行解耦。
下面的方式只是經(jīng)驗總結(jié),并不是唯一或者最佳實現(xiàn):
思路:使用全局狀態(tài)管理所有對話框
對話框本質(zhì)上是獨立于其他界面的一個窗口,用于完成一個獨立的功能。
所以,定義一個對話框,定位等價于定義一個具有唯一URL路徑的頁面。只是前者由彈出層實現(xiàn),后者是頁面的切換。
對話框UI彈出過程和頁面URL的切換非常類似,那么我們就可以給每一個對話框定義一個全局唯一的ID,然后通過這個ID去顯示或者隱藏一個對話框,并且給它傳遞參數(shù)。
嘗試設(shè)計一個API去做對話框的全局管理
假設(shè)我們實現(xiàn)的對話框為NiceModal,那么我們的目標(biāo)是如下去使用:
const UserInfoModal = NiceModal.create(
'user-info-modal',
RealUserInfoModal
)
// 創(chuàng)建一個useNiceModal 這樣的hook去獲取某個id的對話框的操作對象
const modal = useNiceModal('user-info-modal')
// 通過 modal.show 顯示一個對話框,并能夠給它傳遞參數(shù)
modal.show(args)
modal.hide()
可以看到,如果有這樣的API,那么無論在哪個層級的組件,只要知道某個Modal的ID,那么就都可以統(tǒng)一使用這些對話框,而不再需要考慮該在哪個層級的組件去定義了。
實現(xiàn):創(chuàng)建NiceModal組件和相關(guān)API
創(chuàng)建一個處理所有對話框的action creator 和 reducer
function showModal(modalId, args) {
return {
type: "nice-modal/show",
payload: {
modalId,
args
}
}
}
function hideModal(modalId, force) {
return {
type: "nice-modal/hide",
payload: {
modalId,
force
}
}
}
const modalReducer = (state = { hiding: {} }, action) {
switch (action.type) {
case "nice-modal/show":
const {modalId, args} = action.payload
return {
...state,
// 如果存在 modalId 對應(yīng)的狀態(tài)(即args),就顯示這個對話框
// 只要有參數(shù)就認(rèn)為對話框應(yīng)該顯示,如果沒有傳遞args,在reducer中使用默認(rèn)值true
[modalId]: args || true,
// 定義一個hiding 狀態(tài), 用于處理對話框關(guān)閉動畫
hiding: {
...state.hiding,
[modalId]: false,
}
}
case "nice-modal/hide":
const { modalId, force: boolean } = action.payload
// 只有force時才真正移除對話框,否則就是隱藏中hiding
return action.payload.force
? {
...state,
[modalId]: false,
hiding: { [modalId]: false }
}
: { ...state, hiding: { [modalId]: true } }
default:
return state
}
}
這段代碼的主要思路就是通過Redux的store去存儲每個對話框狀態(tài)和參數(shù)。在這里設(shè)計了兩個action,分別顯示和隱藏對話框。
特別注意的是,這里加入了hiding這樣的一個狀態(tài),用來處理對話框關(guān)閉過程動畫。
根據(jù)使用順序,首先實現(xiàn) createNiceModal,
使用容器模式,在對話框不可見時直接返回null,從而不渲染任何內(nèi)容,
確保即使頁面上定義了100個對話框,也不會影響性能。
createNiceModal = (modalId, Comp) => {
return (props) => {
const { visible, args } = useNiceModal(modalId)
if (!visible) return null
return <Comp {...args} {...props} />
}
}
// 使用
const MyModal = createNiceModal('my-modal', () => {
return (
<NiceModal id="my-modal" title="Nice modal">
Hello NiceModal
</NiceModal>
)
})
實現(xiàn)useNiceModal,根據(jù)id,封裝一些邏輯。
讓Redux的action使用起來更方便,在其內(nèi)部封裝對store的操作,從而實現(xiàn)對話框狀態(tài)管理的邏輯重用。
const modalCallbacks = {}
const useNiceModal = (modalId) => {
const dispatch = useDispatch()
// 封裝Redux action 用于顯示對話框
const show = useCallback(
(args) => {
dispatch(showModal(modalId, args))
},
[dispatch, modalId]
)
// 封裝Redux action 用于隱藏對話框 (force: boolean)
const hide = useCallback(
(force) => {
dispatch(hideModal(modalId, force))
},
[dispatch, modalId]
)
const args = useSelector((s) => s[modalId])
const hiding = useSelector((s) => s.hiding[modalId])
// 只要有參數(shù)就認(rèn)為對話框應(yīng)該顯示,如果沒有傳遞args,在reducer中使用默認(rèn)值true
return { args, hiding, visible: !!args, show, hide }
}
這樣,我們就實現(xiàn)了一個NiceModal這樣的全局對話管理框架。
這樣使用:
import { Button } from 'antd'
import NiceModal, {
createNiceModal,
useNiceModal
} from "./NiceModal"
const MyModal = createNiceModal("my-modal", () => {
return (
<NiceModal id="my-modal" title="Nice Modal">
Hello World
</NiceModal>
)
})
function MyModalExample() {
const modal = useNiceModal("my-modal")
return (
<>
<Button type="primary" onClick={() => modal.show()}>
Show my modal
</Button>
<MyModal />
</>
)
}
處理對話框的返回值
如果說對話框和頁面這兩種UI模式基本上是一致的,都是獨立窗口完成獨立邏輯。但是在用戶交互上,有一定的差別:
- 對話框可能需要返回值給調(diào)用者
- 而頁面切換一般不會關(guān)心頁面執(zhí)行的結(jié)果是什么
基于上面的NiceModal實現(xiàn)邏輯,現(xiàn)在考慮如何讓調(diào)用者獲得返回值。
我們可以把用戶在對話框中的操作看成一個異步操作邏輯,那么用戶在完成對話框中內(nèi)容的操作后,就認(rèn)為異步操作邏輯完成了。因此我們可以利用Promise來完成這樣的邏輯。
那么,我們要實現(xiàn)的API如下:
const modal = useNiceModal('my-modal')
// 實現(xiàn)一個 promise API 來處理返回值
modal.show(args).then(res => {})
事實上,要實現(xiàn)這樣一個機(jī)制并不困難,就是在 useNiceModal 這個 Hook 的實現(xiàn)中提供一個 modal.resolve 這樣的方法,能夠去 resolve modal.show 返回的 Promise。
代碼的核心思路就是將show 和 resolve 兩個函數(shù)通過 Promise 聯(lián)系起來。因此兩個函數(shù)調(diào)用位置不一樣,所以我們使用一個局部的臨時變量,來存放resolve回調(diào)函數(shù)。
// 使用一個 object 緩存 promise 的 resolve 回調(diào)函數(shù)
const modalCallbacks = {};
export const useNiceModal = (modalId) => {
const dispatch = useDispatch();
const show = useCallback(
(args) => {
return new Promise((resolve) => {
// 顯示對話框時,返回 promise 并且將 resolve 方法臨時存起來
modalCallbacks[modalId] = resolve;
dispatch(showModal(modalId, args));
});
},
[dispatch, modalId],
);
const resolve = useCallback(
(args) => {
if (modalCallbacks[modalId]) {
// 如果存在 resolve 回調(diào)函數(shù),那么就調(diào)用
modalCallbacks[modalId](args);
// 確保只能 resolve 一次
delete modalCallbacks[modalId];
}
},
[modalId],
);
// 其它邏輯...
// 將 resolve 也作為返回值的一部分
return { show, hide, resolve, visible, hiding };
};
總結(jié)
原文鏈接:https://blog.csdn.net/qq_34895059/article/details/118766726
相關(guān)推薦
- 2022-12-25 Redis過期鍵與內(nèi)存淘汰策略深入分析講解_Redis
- 2021-12-10 C#實現(xiàn)簡易灰度圖和酷炫HeatMap熱力圖winform(附DEMO)_C#教程
- 2022-06-17 Android性能優(yōu)化之圖片大小,尺寸壓縮綜合解決方案_Android
- 2023-02-10 利用Python獲取文件夾下所有文件實例代碼_python
- 2023-01-14 Go?庫bytes.Buffer和strings.Builder使用及性能對比_Golang
- 2022-06-16 詳解Flutter中網(wǎng)絡(luò)框架dio的二次封裝_Android
- 2022-12-07 React元素與組件的區(qū)別示例詳解_React
- 2022-10-08 Qt動態(tài)庫調(diào)用宿主進(jìn)程中的對象方法純虛函數(shù)使用_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支