您好,登錄后才能下訂單哦!
這篇文章主要介紹“React項(xiàng)目中hook怎么實(shí)現(xiàn)展示對(duì)話框功能”,在日常操作中,相信很多人在React項(xiàng)目中hook怎么實(shí)現(xiàn)展示對(duì)話框功能問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”React項(xiàng)目中hook怎么實(shí)現(xiàn)展示對(duì)話框功能”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
React中使用對(duì)話框并不容易,主要因?yàn)椋?/p>
對(duì)話框需要在父組件中聲明,才能在子組件中控制其是否顯示
給對(duì)話框傳遞參數(shù)只能由props傳入,這意味著所有狀態(tài)管理都必須在更高階的組件中。而實(shí)際上這個(gè)對(duì)話框的參數(shù)只在子組件中才會(huì)維護(hù)。這時(shí)就需要我們使用自定義事件將參數(shù)傳回
這些問題的本質(zhì)就是:如何用一個(gè)統(tǒng)一的方式去管理對(duì)話框,從而讓對(duì)話框相關(guān)的業(yè)務(wù)邏輯更加模塊化,以及和其他業(yè)務(wù)邏輯進(jìn)行解耦。
下面的方式只是經(jīng)驗(yàn)總結(jié),并不是唯一或者最佳實(shí)現(xiàn):
對(duì)話框本質(zhì)上是獨(dú)立于其他界面的一個(gè)窗口,用于完成一個(gè)獨(dú)立的功能。
所以,定義一個(gè)對(duì)話框,定位等價(jià)于定義一個(gè)具有唯一URL路徑的頁面。只是前者由彈出層實(shí)現(xiàn),后者是頁面的切換。
對(duì)話框UI彈出過程和頁面URL的切換非常類似,那么我們就可以給每一個(gè)對(duì)話框定義一個(gè)全局唯一的ID,然后通過這個(gè)ID去顯示或者隱藏一個(gè)對(duì)話框,并且給它傳遞參數(shù)。
假設(shè)我們實(shí)現(xiàn)的對(duì)話框?yàn)镹iceModal,那么我們的目標(biāo)是如下去使用:
const UserInfoModal = NiceModal.create( 'user-info-modal', RealUserInfoModal ) // 創(chuàng)建一個(gè)useNiceModal 這樣的hook去獲取某個(gè)id的對(duì)話框的操作對(duì)象 const modal = useNiceModal('user-info-modal') // 通過 modal.show 顯示一個(gè)對(duì)話框,并能夠給它傳遞參數(shù) modal.show(args) modal.hide()
可以看到,如果有這樣的API,那么無論在哪個(gè)層級(jí)的組件,只要知道某個(gè)Modal的ID,那么就都可以統(tǒng)一使用這些對(duì)話框,而不再需要考慮該在哪個(gè)層級(jí)的組件去定義了。
創(chuàng)建一個(gè)處理所有對(duì)話框的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 對(duì)應(yīng)的狀態(tài)(即args),就顯示這個(gè)對(duì)話框 // 只要有參數(shù)就認(rèn)為對(duì)話框應(yīng)該顯示,如果沒有傳遞args,在reducer中使用默認(rèn)值true [modalId]: args || true, // 定義一個(gè)hiding 狀態(tài), 用于處理對(duì)話框關(guān)閉動(dòng)畫 hiding: { ...state.hiding, [modalId]: false, } } case "nice-modal/hide": const { modalId, force: boolean } = action.payload // 只有force時(shí)才真正移除對(duì)話框,否則就是隱藏中hiding return action.payload.force ? { ...state, [modalId]: false, hiding: { [modalId]: false } } : { ...state, hiding: { [modalId]: true } } default: return state } }
這段代碼的主要思路就是通過Redux的store去存儲(chǔ)每個(gè)對(duì)話框狀態(tài)和參數(shù)。在這里設(shè)計(jì)了兩個(gè)action,分別顯示和隱藏對(duì)話框。
特別注意的是,這里加入了hiding這樣的一個(gè)狀態(tài),用來處理對(duì)話框關(guān)閉過程動(dòng)畫。
根據(jù)使用順序,首先實(shí)現(xiàn) createNiceModal,
使用容器模式,在對(duì)話框不可見時(shí)直接返回null,從而不渲染任何內(nèi)容,
確保即使頁面上定義了100個(gè)對(duì)話框,也不會(huì)影響性能。
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> ) })
實(shí)現(xiàn)useNiceModal,根據(jù)id,封裝一些邏輯。
讓Redux的action使用起來更方便,在其內(nèi)部封裝對(duì)store的操作,從而實(shí)現(xiàn)對(duì)話框狀態(tài)管理的邏輯重用。
const modalCallbacks = {} const useNiceModal = (modalId) => { const dispatch = useDispatch() // 封裝Redux action 用于顯示對(duì)話框 const show = useCallback( (args) => { dispatch(showModal(modalId, args)) }, [dispatch, modalId] ) // 封裝Redux action 用于隱藏對(duì)話框 (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)為對(duì)話框應(yīng)該顯示,如果沒有傳遞args,在reducer中使用默認(rèn)值true return { args, hiding, visible: !!args, show, hide } }
這樣,我們就實(shí)現(xiàn)了一個(gè)NiceModal這樣的全局對(duì)話管理框架。
這樣使用:
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 /> </> ) }
如果說對(duì)話框和頁面這兩種UI模式基本上是一致的,都是獨(dú)立窗口完成獨(dú)立邏輯。但是在用戶交互上,有一定的差別:
對(duì)話框可能需要返回值給調(diào)用者
而頁面切換一般不會(huì)關(guān)心頁面執(zhí)行的結(jié)果是什么
基于上面的NiceModal實(shí)現(xiàn)邏輯,現(xiàn)在考慮如何讓調(diào)用者獲得返回值。
我們可以把用戶在對(duì)話框中的操作看成一個(gè)異步操作邏輯,那么用戶在完成對(duì)話框中內(nèi)容的操作后,就認(rèn)為異步操作邏輯完成了。因此我們可以利用Promise來完成這樣的邏輯。
那么,我們要實(shí)現(xiàn)的API如下:
const modal = useNiceModal('my-modal') // 實(shí)現(xiàn)一個(gè) promise API 來處理返回值 modal.show(args).then(res => {})
事實(shí)上,要實(shí)現(xiàn)這樣一個(gè)機(jī)制并不困難,就是在 useNiceModal 這個(gè) Hook 的實(shí)現(xiàn)中提供一個(gè) modal.resolve 這樣的方法,能夠去 resolve modal.show 返回的 Promise。
代碼的核心思路就是將show 和 resolve 兩個(gè)函數(shù)通過 Promise 聯(lián)系起來。因此兩個(gè)函數(shù)調(diào)用位置不一樣,所以我們使用一個(gè)局部的臨時(shí)變量,來存放resolve回調(diào)函數(shù)。
// 使用一個(gè) object 緩存 promise 的 resolve 回調(diào)函數(shù) const modalCallbacks = {}; export const useNiceModal = (modalId) => { const dispatch = useDispatch(); const show = useCallback( (args) => { return new Promise((resolve) => { // 顯示對(duì)話框時(shí),返回 promise 并且將 resolve 方法臨時(shí)存起來 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 }; };
到此,關(guān)于“React項(xiàng)目中hook怎么實(shí)現(xiàn)展示對(duì)話框功能”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。