自定義protal組件簿晓,使用dom-align來定位下拉框

業(yè)務(wù)場景:如下圖始鱼,要求input能輸入,能遠程搜索鸯旁,出現(xiàn)下拉框噪矛,下拉框中的內(nèi)容高度自定義:既可以選中到input,又能點擊具體結(jié)果跳轉(zhuǎn)頁面铺罢。雖然ant的select能做的東西很多艇挨,但是還是達不到我想要的效果,那就自行動手

場景一下圖描述
企業(yè)微信截圖_16276264882801.png
場景二下圖描述
企業(yè)微信截圖_1627626415326.png

解決思路:

(1) 仔細觀察ant的select組件并模仿:選中input利用createPortal 在body下面生成dom韭赘,也就是一個下拉組件
(2)利用dom-align定位到input下面:ant Select基于rc-select缩滨,rc-select的下拉框基于rc-trigger,rc-trigger的下拉框定位基于第三方庫dom-align泉瞻。
(3)點擊其他區(qū)域要隱藏下拉框脉漏,點擊下拉區(qū)域和input不隱藏。使用起來要求跟使用Modal一樣方便

思路已定袖牙,我們來寫代碼

1. protals組件,實現(xiàn)在body下面掛載組件侧巨,組件銷毀也會從body移除。這是基礎(chǔ)部分鞭达,可以在此基礎(chǔ)上再創(chuàng)造各種在body下的組件
import { useLayoutEffect, useRef } from 'react'
import { createPortal } from 'react-dom'

type ProtalsProps = {
 children: any
}

const Protals = ({ children }: ProtalsProps) => {
 const containerRef = useRef<HTMLDivElement | null>(null)

 if (!containerRef.current) {
   containerRef.current = document.createElement('div')
   document.body.appendChild(containerRef.current)
 }

 useLayoutEffect(() => {
   return () => {
     const node = containerRef.current
     if (node) {
       document.body.removeChild(node)
     }
   }
 }, [])
 return createPortal(children, containerRef.current)
}

export default Protals

2. 創(chuàng)造定位組件alignRender司忱,利用dom-align用來定位input和下拉框。該組件擁有顯示碉怔,隱藏的方法烘贴,利用visible來實現(xiàn)首次掛載禁添,基礎(chǔ)樣式也直接拿select的樣式撮胧,嘿嘿
// alignRender.tsx
import { FC, useEffect, useImperativeHandle, useRef } from 'react'
import Protals from '../protals'
import domAlign from 'dom-align'

type AlignRenderProps = {
  children: any
  targetNode: any
  childRef?: any
  points?: any[]
  offset?: any[]
  // 顯示掛載,首次設(shè)置為true即可
  visible: boolean
}

const AlignRender: FC<AlignRenderProps> = ({
  children,
  visible,
  targetNode,
  childRef,
  points = ['tl', 'bl'],
  offset = ['0', '0'],
}) => {
  const sourceNode: any = useRef<HTMLDivElement | null>(null)
  const open = () => {
    domAlign(sourceNode.current, targetNode.current, {
      points,
      offset,
    })
  }
  const close = () => {
    domAlign(sourceNode.current, targetNode.current, {
      points,
      offset: ['-1000%', '0'],
    })
  }
  // 暴露隱藏和顯示的方法
  useImperativeHandle(childRef, () => ({
    open,
    close,
  }))
  function clickCallback(e: Event) {
    if (
      sourceNode.current.contains(e.target) ||
      targetNode.current.contains(e.target)
    ) {
      return
    }

    close()
  }
監(jiān)聽document老翘,只有點擊下拉和input才不關(guān)閉
  useEffect(() => {
    if (visible) {
      open()
      document.addEventListener('click', clickCallback, false)
      return () => {
        document.removeEventListener('click', clickCallback, false)
      }
    }
  }, [visible])
  if (!visible) return null

  return (
    <Protals>
      <div
        ref={sourceNode}
        style={{ padding: '0' }}
        className={'ant-select-dropdown'}
      >
        {children}
      </div>
    </Protals>
  )
}

export default AlignRender

ok芹啥。到這里我們就實現(xiàn)了些業(yè)務(wù)的基石∑糖停可以實現(xiàn)多中定位場景墓怀。

3. 使用組件AlignRender ,下面的業(yè)務(wù)需要各種參數(shù)的傳遞,我這里使用useContext實現(xiàn)卫键,所以下面使用了useMemo傀履,具體可以看我的這篇文章優(yōu)化使用useContext + useReducer 做數(shù)據(jù)管理
const SearchSelect: FC = () => {
 const [visible, setVisible] = useState<boolean>(false)
 const { type, keyWords } = useBarState()
 const dispatch = useDispatchBarState()
 const [value, setValue] = useState<string>('')
 const targetNode: any = useRef<HTMLDivElement | null>(null)
 const childRef: any = useRef()
 // 切換國內(nèi)外
 useEffect(() => {
   if (keyWords && visible) {
     childRef.current.open()
   }
 }, [type])

 return useMemo(() => {
   const changeInput = ({ target }: any) => {
     childRef.current.open()
     setValue(target.value)
     // 如果更改詞匯,則去掉城市id
     dispatch &&
       dispatch({
         keyWords: target.value,
         cityId: '',
         stateId: '',
         countryId: '',
       })
   }
   const AlignProp = {
     visible,
     targetNode,
     childRef,
   }
   return (
     <div className={'relative inline-block'} ref={targetNode}>
       <Input
         className={styles.input}
         bordered={false}
         value={value}
         placeholder="城市/酒店名稱"
         onChange={changeInput}
         onFocus={() => {
           if (visible) {
             childRef.current.open() // 再次點擊
           } else {
             setVisible(true) //visible置為true莉炉,首次掛載到body
           }
         }}
       />
       <AlignRender {...AlignProp}>
         <div className={classnames(value ? 'hidden' : 'block')}>
           <DistrictTab {...districtProps} />
         </div>
         <div className={classnames(value ? 'block' : 'hidden')}>
           <Hotels {...districtProps} />
         </div>
       </AlignRender>
     </div>
   )
 }, [keyWords, visible, value])
}

不僅僅是這個下拉組件钓账,另有一個業(yè)務(wù)場景我也用到了該組件:點擊按鈕碴犬,按鈕下方出現(xiàn)日歷。
有興趣可以看我這邊日歷遇到的問題ant使用calender自定義頭部梆暮,區(qū)分onPanelChange onSelect 事件

以上就是思考解決問題的全部服协,文章很短,實現(xiàn)的過程確實一步步摸索得來啦粹,先寫成一坨偿荷,再逐步拆分優(yōu)化。這個AlignRender 的封裝實現(xiàn)在后面能做很多事情唠椭,比如自定義modal跳纳,tooptip等等,希望對你有幫助

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贪嫂,一起剝皮案震驚了整個濱河市棒旗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撩荣,老刑警劉巖铣揉,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異餐曹,居然都是意外死亡逛拱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門台猴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朽合,“玉大人,你說我怎么就攤上這事饱狂〔懿剑” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵休讳,是天一觀的道長讲婚。 經(jīng)常有香客問我,道長俊柔,這世上最難降的妖魔是什么筹麸? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮雏婶,結(jié)果婚禮上物赶,老公的妹妹穿的比我還像新娘。我一直安慰自己留晚,他們只是感情好酵紫,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般奖地。 火紅的嫁衣襯著肌膚如雪状蜗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天鹉动,我揣著相機與錄音轧坎,去河邊找鬼。 笑死泽示,一個胖子當(dāng)著我的面吹牛缸血,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播械筛,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼捎泻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了埋哟?” 一聲冷哼從身側(cè)響起笆豁,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赤赊,沒想到半個月后闯狱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡抛计,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年哄孤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吹截。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘦陈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出波俄,到底是詐尸還是另有隱情晨逝,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布懦铺,位于F島的核電站捉貌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏阀趴。R本人自食惡果不足惜昏翰,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一苍匆、第九天 我趴在偏房一處隱蔽的房頂上張望刘急。 院中可真熱鬧,春花似錦浸踩、人聲如沸叔汁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽据块。三九已至码邻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間另假,已是汗流浹背像屋。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留边篮,地道東北人己莺。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像戈轿,于是被迫代替她去往敵國和親凌受。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一思杯、自定義原因 1.項目定制化需求胜蛉,下拉框的樣式包括滾動條都需要同現(xiàn)有客戶端(QT qml開發(fā))版本保持一致;2....
    小魔女_b68a閱讀 309評論 0 1
  • 這次需求里面需要自定義下拉選擇框色乾,所以自己用vue寫了個下拉選擇框誊册。 開發(fā)過程中主要解決的是點擊其他空白地方,下拉...
    AAA前端閱讀 5,605評論 0 3
  • 最近接手了一個新項目暖璧,設(shè)計獅要求嚴(yán)格按照設(shè)計稿來解虱,so,只能放棄UI框架漆撞,自己寫組件了殴泰,略苦逼。 平時用的最多的是...
    徐向博閱讀 2,408評論 0 0
  • 在處理表單的網(wǎng)頁里,原生的select標(biāo)簽可以滿足功能至会,但是現(xiàn)在的產(chǎn)品一般對用戶體驗及設(shè)計要求比較高离咐,比如下拉框后...
    菜菜___閱讀 3,301評論 0 7
  • 今天做h5遇到個問題,我發(fā)現(xiàn)h5原生的下拉框很不好用奉件,而且更改樣式也不好=改宵蛀。所以我索性自己想辦法自定義了一個下拉...
    為夢想而努力奮斗閱讀 10,447評論 0 1