国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React造輪系列:對(duì)話框組件 - Dialog 思路

qianfeng / 1646人閱讀

摘要:本文是造輪系列第二篇。實(shí)現(xiàn)方式事件處理跟差不多,唯一多了一步就是當(dāng)點(diǎn)擊或者的時(shí)候,如果外部有回調(diào)就需要調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。

本文是React造輪系列第二篇。

1.React 造輪子系列:Icon 組件思路

本輪子是通過 React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。

想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!

UI

對(duì)話框一般是我們點(diǎn)擊按鈕彈出的這么一個(gè)東西,主要類型有 Alter, ConfirmModal, Modal 一般帶有半透明的黑色背景。當(dāng)然外觀可參考 AntD 或者 Framework 等。

確定 API

API 方面主要還是要參考同行,因?yàn)槿绻幸惶?,別人想你用的UI框架時(shí),你的 API 跟他之前常用的又不用,這樣就加大了入門門檻,所以API 盡量保持跟現(xiàn)有的差不多。

對(duì)話框除了提供顯示屬性外,還要有點(diǎn)擊確認(rèn)后的回放函數(shù),如:

alert("你好").then(fn)
confirm("確定?").then(fn)
modal(組件名)

實(shí)現(xiàn)

Dialog 源碼已經(jīng)上傳到這里。

dialog/dialog.example.tsx, 這里 state ,生命周期使用 React 16.8 新出的 Hook,如果對(duì) Hook 不熟悉可以先看官網(wǎng)文檔。

dialog/dialog.example.tsx

import React, {useState} from "react"
import Dialog from "./dialog"
export default function () {
  const [x, setX] = useState(false)
  return (
    
) }

dialog/dialog.tsx

import React from "react"

interface Props {
  visible: boolean
}

const Dialog: React.FunctionComponent = (props) => {
  return (
    props.visible ? 
      
dialog
: null ) } export default Dialog

運(yùn)行效果

顯示內(nèi)容

上述還有問題,我們 dialog 在組件內(nèi)是寫死的,我們想的是直接通過組件內(nèi)包裹的內(nèi)容,如:

// dialog/dialog.example.tsx
...

  hi

...

這樣寫,頁面上是不會(huì)顯示 hi 的,這里 children 屬性就派上用場(chǎng)了,我們需要在 dialog 組件中進(jìn)一步驟修改如下內(nèi)容:

// dialog/dialog.tsx
...
return (
    props.visible ? 
      
{props.children}
: null ) ...
顯示遮罩

通常對(duì)話框會(huì)有一層遮罩,通常我們大都會(huì)這樣寫:

// dialog/dialog.tsx
...
props.visible ? 
  
{props.children}
: null ...

這種結(jié)構(gòu)有個(gè)不好的地方就是點(diǎn)擊遮罩層的時(shí)候要關(guān)閉對(duì)話框,如果是用這種結(jié)構(gòu),用戶點(diǎn)擊任何 div,都相當(dāng)于點(diǎn)擊遮罩層,所以最好要分開:

// dialog/dialog.tsx
...
{props.children}
...

由于 React 要求最外層只能有一個(gè)元素, 所以我們多用了一個(gè) div 包裹起來,但是這種方法無形之中多了個(gè) div,所以可以使用 React 16 之后新出的 Fragment, Fragment 跟 vue 中的 template 一樣,它是不會(huì)渲染到頁面的。

import React, {Fragment} from "react"
import "./dialog.scss";
interface Props {
  visible: boolean
}

const Dialog: React.FunctionComponent = (props) => {
  return (
    props.visible ? 
     
        
{props.children}
: null ) } export default Dialog
完善頭部,內(nèi)容及底部

這里不多說,直接上代碼

import React, {Fragment} from "react"
import "./dialog.scss";
import {Icon} from "../index"
interface Props {
  visible: boolean
}

const Dialog: React.FunctionComponent = (props) => {
  return (
    props.visible ? 
      
          
提示
{props.children}
: null ) } export default Dialog

從上述代碼我們可以發(fā)現(xiàn)我們寫樣式的名字時(shí)候,為了不被第三使用覆蓋,我們自定義了一個(gè) fui-dialog前綴,在寫每個(gè)樣式名稱時(shí),都要寫一遍,這樣顯然不太合理,萬一哪天我不用這個(gè)前綴時(shí)候,每個(gè)都要改一遍,所以我們需要一個(gè)方法來封裝。

咱們可能會(huì)寫這樣方法:

function scopedClass(name) {
  return `fui-dialog-${name}`
}

這樣寫不行,因?yàn)槲覀?name 可能不傳,這樣就會(huì)多出一個(gè) -,所以需要進(jìn)一步的判斷:

function scopedClass(name) {

  return `fui-dialog-${name ? "-" + name : ""}`
}

那還有沒有更簡(jiǎn)潔的方法,使用 filter 方法:

function scopedClass(name ?: string) {
  return ["fui-dialog", name].filter(Boolean).join("-")
}

調(diào)用方式如下:

  ....
  
      
提示
{props.children}
...

大家在想法,這樣寫是有問題,每個(gè)組件都寫一個(gè)函數(shù)嗎,如果 Icon 組件,我還需要寫一個(gè) fui-icon, 解決方法是把 前綴當(dāng)一個(gè)參數(shù),如:

function scopedClass(name ?: string) {
  return ["fui-dialog", name].filter(Boolean).join("-")
}

調(diào)用方式如下:

className={scopedClass("fui-dialog", "mask")}

這樣寫,還不如直接寫樣式,這種方式是等于白寫了一個(gè)方法,那怎么辦?這就需要高階函數(shù)出場(chǎng)了。實(shí)現(xiàn)如下:

function scopeClassMaker(prefix: string) {
  return function (name ?: string) {
    return [prefix, name].filter(Boolean).join("-")
  }
}

const scopedClass = scopeClassMaker("fui-dialog")

scopeClassMaker 函數(shù)是高級(jí)函數(shù),返回一個(gè)帶了 prefix 參數(shù)的函數(shù)。

事件處理

在寫事件處理之前,我們 Dialog 需要接收一個(gè) buttons 屬性,就是顯示的操作按鈕并添加事件:

// dialog/dialog.example.tsx
...
 {setX(false)}}>1,
    ,
  ]
}>
  
hi
...

咱們看到這個(gè),第一反應(yīng)應(yīng)該是覺得這樣寫很麻煩,我寫個(gè) dialog, visible要自己,按鈕要自己,連事件也要自己寫。請(qǐng)接受這種設(shè)定。雖然麻煩,但非常的好理解。這跟 Vue 的理念是不太一樣的。當(dāng)然后面會(huì)進(jìn)一步驟優(yōu)化。

組件內(nèi)渲染如下:

{ props.buttons }

運(yùn)行起來你會(huì)發(fā)現(xiàn)有個(gè)警告:

主要是說我們渲染數(shù)組時(shí),需要加個(gè) key,解決方法有兩種,就是不要使用數(shù)組方式,當(dāng)然這不治本,所以這里 React.cloneElemen 出場(chǎng)了,它可以克隆元素并添加對(duì)應(yīng)的屬性值,如下:

{
  props.buttons.map((button, index) => {
    React.cloneElement(button, {key: index})
  })
}

對(duì)應(yīng)的點(diǎn)擊關(guān)閉事件相對(duì)容易這邊就不講了,可以自行查看源碼。

接下來來看一個(gè)樣式的問題,首先先給出我們遮罩的樣式:

.fui-dialog {
  position: fixed; background: white; min-width: 20em;
  z-index: 2;
  border-radius: 4px; top: 50%; left: 50%; transform: translate(-50%, -50%);
  &-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%;
    background: fade_out(black, 0.5);
    z-index: 1;
  }
  .... 以下省略其它樣式
}

我們遮罩 .fui-dialog-mask 使用 fixed 定位感覺是沒問題的,那如果在調(diào)用 dialog 同級(jí)在加以下這么元素:

666
...

運(yùn)行效果:

發(fā)現(xiàn)遮罩并沒有遮住 666 的內(nèi)容。這是為什么?

看結(jié)構(gòu)也很好理解,遮罩元素與 666 是同級(jí)結(jié)構(gòu),且層級(jí)比 666 低,當(dāng)然是覆蓋不了的。那咱們可能就會(huì)這樣做,給.fui-dialog-mask設(shè)置一個(gè) zIndex 比它大的唄,如 9999

效果:

恩,感覺沒問題,這時(shí)我們?cè)?Dialog 組件在嵌套一層 zIndex 為 9 的呢,如:

...

運(yùn)行效果如下:

發(fā)現(xiàn),父元素被壓住了,里面元素 zIndex 值如何的高,都沒有效果。

那這要怎么破?答案是不要讓它出現(xiàn)在任何元素的里面,這怎么可能呢。這里就需要引出一個(gè)神奇的 API了。這個(gè) API 叫做 傳送門(portal)。

用法如下:

return ReactDOM.createPortal(
  this.props.children,
  domNode
);

第一個(gè)參數(shù)就是你的 div,第二個(gè)參數(shù)就是你要去的地方。

import React, {Fragment, ReactElement} from "react"
import ReactDOM from "react-dom"
import "./dialog.scss";
import {Icon} from "../index"
import {scopedClassMaker} from "../classes"

interface Props {
  visible: boolean,
  buttons: Array,
  onClose: React.MouseEventHandler,
  closeOnClickMask?: boolean
}

const scopedClass = scopedClassMaker("fui-dialog")
const sc = scopedClass

const Dialog: React.FunctionComponent = (props) => {

  const onClickClose: React.MouseEventHandler = (e) => {
    props.onClose(e)
  }
  const onClickMask: React.MouseEventHandler = (e) => {
    if (props.closeOnClickMask) {
      props.onClose(e)
    }
  }
  const x = props.visible ? 
  
      
提示
{props.children}
{ props.buttons.map((button, index) => { React.cloneElement(button, {key: index}) }) }
: null return ( ReactDOM.createPortal(x, document.body) ) } Dialog.defaultProps = { closeOnClickMask: false } export default Dialog

運(yùn)行效果:

當(dāng)然這樣,如果 Dialog 層級(jí)比同級(jí)的 zIndex 小的話,還是覆蓋不了。 那 zIndex 一般設(shè)置成多少比較合理。一般 Dialog 這層設(shè)置成 1, mask 這層設(shè)置成2。定的越小越好,因?yàn)橛脩艨梢匀ジ摹?/p> zIndex 的管理

zIndex 管理一般就是前端架構(gòu)師要做的了,根據(jù)業(yè)務(wù)產(chǎn)景來劃分,如廣告肯定是要在頁面最上面,所以 zIndex 一般是屬于最高級(jí)的。

便利的 API 之 Alert

上述我們使用 Dialog 組件調(diào)用方式比較麻煩,寫了一堆,有時(shí)候我們想到使用 alert 直接彈出一個(gè)對(duì)話框這樣簡(jiǎn)單方便。如

  

example 3

我們想直接點(diǎn)擊 button ,然后彈出我們自定義的對(duì)話框內(nèi)容為1 ,需要在 Dialog 組件內(nèi)我們需要導(dǎo)出一個(gè) alert 方法,如下:

// dialog/dialog.tsx
...
const alert = (content: string) => {
  const component =  {}}>
    {content}
  
  const div = document.createElement("div")
  document.body.append(div)
  ReactDOM.render(component, div)
}

export {alert}
...

運(yùn)行效果:

但有個(gè)問題,因?yàn)閷?duì)話框的 visible 是由外部傳入的,且 React 是單向數(shù)據(jù)流的,在組件內(nèi)并不能直接修改 visible,所以在 onClose 方法我們需要再次渲染一個(gè)新的組件,并設(shè)置新組件 visibleture,覆蓋原來的組件:

...
const alert = (content: string) => {
  const component =  {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }}>
    {content}
  
  const div = document.createElement("div")
  document.body.append(div)
  ReactDOM.render(component, div)
}
..

便利的 API 之 confirm

confirm 調(diào)用方式:


第一個(gè)參數(shù)是顯示的內(nèi)容,每二個(gè)參數(shù)是確認(rèn)的回調(diào),第三個(gè)參數(shù)是取消的回調(diào)函數(shù)。

實(shí)現(xiàn)方式:

const confirm = (content: string, yes?: () => void, no?: () => void) => {
  const onYes = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
    yes && yes()
  }
  const onNo = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
    no && no()
  }
  const component = (
   { onNo()}}
    buttons={[, 
              
            ]}
  >
    {content}
  )
  const div = document.createElement("div")
  document.body.appendChild(div)
  ReactDOM.render(component, div)
}

事件處理跟 Alter 差不多,唯一多了一步就是 confirm 當(dāng)點(diǎn)擊 yes 或者 no 的時(shí)候,如果外部有回調(diào)就需要調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。

便利的 API 之 modal

modal 調(diào)用方式:


modal 對(duì)應(yīng)傳遞的內(nèi)容就不是單單的文本了,而是元素。

實(shí)現(xiàn)方式:

const modal = (content: ReactNode | ReactFragment) => {
  const onClose = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }
  const component = 
    {content}
  
  const div = document.createElement("div")
  document.body.appendChild(div)
  ReactDOM.render(component, div)
}

注意,這邊的 content 類型。

運(yùn)行效果:

這還有個(gè)問題,如果需要加按鈕呢,可能會(huì)這樣寫:

  
  )}}>modal

這樣是關(guān)不了的,因?yàn)?Dialog 是封裝在 modal 里面的。如果要關(guān),必須控制 visible,那很顯然我從外面控制不了里面的 visible,所以這個(gè) button 沒有辦法把這個(gè) modal 關(guān)掉。

解決方法就是使用閉包,我們可以在 modal 方法里面把 close 方法返回:

const modal = (content: ReactNode | ReactFragment) => {
  const onClose = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }
  const component = 
    {content}
  
  const div = document.createElement("div")
  document.body.appendChild(div)
  ReactDOM.render(component, div)
  return onClose;
}

最后多了一個(gè) retrun onClose,由于閉包的作用,外部調(diào)用返回的 onClose 方法可以訪問到內(nèi)部變量。

調(diào)用方式:

const openModal = () => {
  const close = modal(

你好

) }
重構(gòu) API

在重構(gòu)之前,我們先要抽象 alert, confirm, modal 中各自的方法:

alert confirm modal
onClose onClose * 2 onClose
component component component
render render render
return api

從表格可以看出,modal 與其它兩個(gè)只多了一個(gè) retrun api,其實(shí)其它兩個(gè)也可以返回對(duì)應(yīng)的 Api,只是我們沒去調(diào)用而已,所以補(bǔ)上:

alert confirm modal
onClose onClose * 2 onClose
component component component
render render render
return api return api return api

這樣一來,這三個(gè)函數(shù)從抽象層面上來看是類似的,所以這三個(gè)函數(shù)應(yīng)該合成一個(gè)。

首先抽取公共部分,先取名為x ,內(nèi)容如下:

const x= (content: ReactNode, buttons ?:Array, afterClose?: () => void) => {
  const close = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
    afterClose && afterClose()
  }
  const component = 
   {
      close(); afterClose && afterClose()
    }}
    buttons={buttons}
  >
    {content}
  
  const div = document.createElement("div")
  document.body.append(div)
  ReactDOM.render(component, div)
  return close
}

alert 重構(gòu)后的代碼如下:

const alert = (content: string) => {
  const button = 
  const close = x(content, [button])
}

confirm 重構(gòu)后的代碼如下:

const confirm = (content: string, yes?: () => void, no?: () => void) => {

  const onYes = () => {
    close()
    yes && yes()
  }
  const onNo = () => {
    close()
    no && no()
  }
  const buttons = [
    , 
    
  ]
  const close =  modal(content, buttons, no)
}

modal 重構(gòu)后的代碼如下:

const modal = (content: ReactNode | ReactFragment) => {
  return x(content)
}

最后發(fā)現(xiàn)其實(shí) x 方法就是 modal 方法,所以更改 x 名為 modal,刪除對(duì)應(yīng)的 modal 定義。

總結(jié)

scopedClass 高階函數(shù)的使用

傳送門 portal

動(dòng)態(tài)生成組件

閉包傳 API

本組件為使用優(yōu)化樣式,如果有興趣可以自行優(yōu)化,本節(jié)源碼已經(jīng)上傳至這里中的lib/dialog。

參考

方應(yīng)杭老師的React造輪子課程

你的點(diǎn)贊是我持續(xù)分享好東西的動(dòng)力,歡迎點(diǎn)贊!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/109833.html

相關(guān)文章

  • React造輪系列:Layout 組件思路

    摘要:本文是造輪系列第三篇。造輪子系列組件思路造輪系列對(duì)話框組件思路想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來篇優(yōu)質(zhì)文章等著你初始化參考組件分別分為五個(gè)組件。參考方應(yīng)杭老師的造輪子課程交流干貨系列文章匯總?cè)缦?,覺得不錯(cuò)點(diǎn)個(gè),歡迎加群互相學(xué)習(xí)。 本文是React造輪系列第三篇。 1.React 造輪子系列:Icon 組件思路 2.React造輪系列:對(duì)話框組件 - Dialog 思路 想閱讀更多優(yōu)質(zhì)...

    neroneroffy 評(píng)論0 收藏0
  • 讀zent源碼庫(kù)之Dialog組件實(shí)現(xiàn)

    摘要:但是,最后一步,事件怎么綁定呢這塊沒有深入研究了,不過我想,應(yīng)該這樣去實(shí)現(xiàn)也是沒有問題的。的具體做法是,把方法放到了一個(gè)叫做的組件上去實(shí)現(xiàn)這個(gè)功能,然后再把內(nèi)容放進(jìn)這個(gè)組件。其他的邏輯比如顯示隱藏之類,全部都放到組件自身上去實(shí)現(xiàn)。 1、Dialog組件提供什么功能,解決什么問題? zent的Dialog組件,使用姿勢(shì)是這樣的(代碼摘自zent官方文檔:https://www.youza...

    陳江龍 評(píng)論0 收藏0
  • 為什么使用react

    摘要:為什么會(huì)慢呢因?yàn)閷?duì)的修改為影響網(wǎng)頁的用戶界面,重繪頁面是一項(xiàng)昂貴的操作。太多的操作會(huì)導(dǎo)致一系列的重繪操作,為了確保執(zhí)行結(jié)果的準(zhǔn)確性,所有的修改操作是按順序同步執(zhí)行的?;亓鞑僮髦饕獣?huì)發(fā)生在幾種情況下當(dāng)對(duì)節(jié)點(diǎn)執(zhí)行新增或者刪除操作時(shí)。 一. 函數(shù)式編程 React 把用戶界面抽象成一個(gè)個(gè)組件,如按鈕組件 Button、對(duì)話框組件 Dialog、日期組件 Calendar。 開發(fā)者通過組...

    Towers 評(píng)論0 收藏0
  • 一款簡(jiǎn)單到極致的 React 數(shù)據(jù)流框架——Refast

    摘要:經(jīng)過派發(fā)器,調(diào)用回調(diào),修改數(shù)據(jù)層。這是一個(gè)循環(huán)往復(fù)的過程,最大的特點(diǎn)是數(shù)據(jù)單向流動(dòng)。是團(tuán)隊(duì)開發(fā)并開源的一款數(shù)據(jù)流框架。它是一些修改組件狀態(tài)的函數(shù)大體與類似,一般也稱之為集合。的第一個(gè)參數(shù)始終是。簡(jiǎn)單到極致,就是我們?cè)O(shè)計(jì)的初衷。 Flux 是 React 框架的好伴侶。它優(yōu)秀的單向數(shù)據(jù)流設(shè)計(jì),使得數(shù)據(jù)的流向更加清晰,能幫助開發(fā)者更好的管理和調(diào)試組件的內(nèi)部狀態(tài)。Facebook 官方出 F...

    laznrbfe 評(píng)論0 收藏0
  • 前端每周清單第 48 期:Slack Webpack 構(gòu)建優(yōu)化,CSS 命名規(guī)范與用戶追蹤,Vue.

    摘要:發(fā)布是由團(tuán)隊(duì)開源的,操作接口庫(kù),已成為事實(shí)上的瀏覽器操作標(biāo)準(zhǔn)。本周正式發(fā)布,為我們帶來了,,支持自定義頭部與腳部,支持增強(qiáng),兼容原生協(xié)議等特性變化。新特性介紹日前發(fā)布了大版本更新,引入了一系列的新特性與提升,本文即是對(duì)這些變化進(jìn)行深入解讀。 showImg(https://segmentfault.com/img/remote/1460000012940044); 前端每周清單專注前端...

    sean 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<