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

資訊專欄INFORMATION COLUMN

一個 React Form 組件的重構思路

Betta / 1619人閱讀

摘要:本文發布于我的博客最近對團隊內部組件庫中的組件進行了重構,記錄一下思考的過程。暴露對外提供整個表單狀態的方法通過在外監聽每次觸發的事件來獲取整個的狀態。子表單數量或類型發生變化時當下面子組件被添加或刪除時,需要及時更新的結構。

本文發布于 我的博客

最近對團隊內部 React 組件庫(ne-rc)中的 Form 組件進行了重構,記錄一下思考的過程。

一些前置定義:

名詞 定義
表單 Form 組件
子表單 嵌套在 Form 下面的類似 Input, Select 這樣的子組件

首先我們看一下,我們的對 Form 組件的需求是什么。

獲取當前變動表單的狀態

校驗所有必填表單是否填寫完成

對外觸發具體表單變化的方法 formFieldChange

暴露對外提供整個表單狀態的方法

提供整個表單最新狀態的方法 $Form.data

提交方法

校驗表單是否通過校驗

對外觸發 formSubmit 方法

接著我們從重構前和重構后,看如何來解決這個問題。

Before 獲取當前變動表單的狀態 如何獲取變動的子表單

React 父子通信需要通過 prop 傳遞方法,對于 Form 下面的類似與 Input 之類的子表單的變化想要通知到父級,如果不借助第三方的事件傳遞方法,那么就只能通過由父級通過 props 向 Input 傳遞 formFieldChange(假設就叫這個名字)方法,然后當子組件變化時去調用 formFieldChange 來實現。

那么問題來了,什么時候去傳遞這個方法呢?

不能在具體頁面里面使用的時候再去每條表單里面注冊這個方法,那每個用到表單組件的時候就都需要給子表單進行這樣的事件綁定,這樣太累了。

所以一開始,我選擇通過直接遞歸的遍歷 Form 下面的 children,只要發現這個 children 是我想要的表單類型,那么就重新克隆一個帶有 formFieldChange 的組件來替換掉原來的組件。

/**
  * 獲取 form 下面每一個表單對象,注入屬性,并收集起來
  * @param children
  * @returns {*}
  */
function getForms(children) {
  return React.Children.map(children, (el, i) => {
    if (!el) {
      return null
    }
    switch (el.type) {
      case Input:
        Forms.push(el)
        return React.cloneElement(
          el,
          {
            key: i,
            formFieldChange,
            emptyInput
          }
        )
      case Select:
        Forms.push(el)
        return React.cloneElement(
          el,
          {
            key: i,
            formFieldChange
          }
        )
      case CheckBox:
        Forms.push(el)
        return React.cloneElement(
          el,
          {
            key: i,
            formFieldChange
          }
        )
      default:
        if (el.props && el.props.children instanceof Array) {
          const children = getForms(el.props.children)
          return React.cloneElement(
            el,
            {
              key: i,
              children
            }
          )
        } else {
          return el
        }
    }
  })
}

這樣,所有的特定子組件就都可以拿到被注冊的方法。以 Input 為例,在 Input 的 onChange 方法里面去調用從父級 props 傳入的 formFieldChange 就可以通知到 Form 組件了。

收集變動表單的數據。

前一步完成后,這一步就比較簡單了,Input 在調用 formFieldChange 的時候把想要傳遞的數據作為參數傳進去,在 Form 里面去對這個參數做處理,就可以拿到當前變動的表單狀態數據了。

校驗表單是否填寫完成

前面我們收集了每一條變動表單的數據。但是要判斷當前 Form 下面的表單是否填寫完成,那么首先需要知道我們有多少個需要填寫的表單,然后在 formFieldChange 的時候進行判斷就可以了。如何來提前知道我們有多少需要填寫的 Field 呢,之前我選擇的是通過在使用 Form 的時候先初始化一個包含所有表單初始化狀態的數據。

export default class Form extends React.Component {
  constructor(props) {
    super(props)
    this.Forms = []
    this.formState = Object.assign({}, {
      isComplete: false,
      isValidate: false,
      errorMsg: "",
      data: {}
    }, this.props.formState)
  }

  static propTypes = {
    onChange: PropTypes.func,
    onSubmit: PropTypes.func,
    formState: PropTypes.object
  }
?// 初始化一個類似這樣的對象傳遞給 Form
formState: {
  data: {
    realName: {},
    cityId: {},
    email: {},
    relativeName: {},
    relativePhone: {},
    companyName: {}
  }
},

這樣就很粗暴的解決了這個問題,但是這中間存在很多問題。

因為限定了特定的組件類型(Input,Select,CheckBox),導致不利于擴展,如果在開發過程遇到其他類型的比如自定義的子表單,那么 Form 就沒法對這個自定義子表單進行數據收集,解決起來比較麻煩。

所以就在考慮另一個種實現方式, Form 只去收集一個特定條件下的組件,只要這個組件滿足了這個條件,并實現了對應的接口,那么 Form 就都可以去收集處理。這樣也就大大挺高了適用性。

暴露對外提供整個表單狀態的方法

通過在外監聽每次 Form 觸發的 onChange 事件來獲取整個 Form 的狀態。

提交方法 檢驗表單是否通過校驗

已經有了整個 Form 的數據對象,做校驗并不是什么困難。通過校驗的時候調用 formSubmit 方法,沒有通過校驗的時候對外把錯誤信息添加到 Form 的 state 上去。

對外觸發 formSubmit 方法

當表單通過校驗的時候,對外觸發 formSubmit 方法,把要提交的數據作為 formSubmit 的參數傳遞給外面。

After

前面是之前寫的 Form 組件的一些思路,在實際使用中也基本能滿足業務需求。

但是整個 Form 的可拓展性比較差,無法很好的接入其他自定義的組件。所以萌生了重寫的想法。

對于重寫的這個 Form,我的想法是:首先一定要方便使用,不需要一大堆的起始工作;其次就是可拓展性要強,除了自己已經提供的內在 Input,Select 等能夠接入 Form 外,對于其他的業務中的特殊需求需要接入 Form 的時候,只要這個組件實現了特定的接口就可以了很方便的接入,而不需要大量的去修改組件內部的代碼。

重構主要集中在上面需求 1 里面的內容,也就是:__獲取當前變動表單的狀態__

獲取當前表單的狀態分解下來有一下幾點:

獲取所有需要收集的子表單 formFields

初始化 Form state

表單下面子表單數量或類型發生變化時更新 1 里面創建的 formFields

子表單內部狀態發生變化時通知到父表單

獲取當前變動表單的狀態 獲取所有需要的子表單

同樣通過遞歸遍歷 children 來獲取需要收集的子表單,通過子表單的 type.name 命名規則是否符合我們的定義來決定是否要進行收集。
直接來看代碼:

collectFormField = (children) => {

  const handleFieldChange = this.handleFieldChange

  // 簡單粗暴,在 Form 更新的時候直接清空上一次保存的 formFields,全量更新,
  // 避免 formFields 內容或者數量發生變化時 this.formFields 數據不正確的問題
  const FormFields = this.formFields = []

  function getChildList(children) {
    return React.Children.map(children, (el, i) => {
      // 只要 Name 以 _Field 開頭,就認為是需要 From 管理的組件
      if (!el || el === null) return null

      const reg = /^_Field/
      const childName = el.type && el.type.name
      if (reg.test(childName)) {
        FormFields.push(el)
        return React.cloneElement(el, {
          key: i,
          handleFieldChange
        })
      } else {
        if (el.props && el.props.children) {
          const children = getChildList(el.props.children)
          return React.cloneElement(el, {
            key: i,
            children
          })
        } else {
          return el
        }
      }
    })
  }

只要組件的 class name 以 _Field 開頭,就把它收集起來,并傳入 handleFieldChange 方法,這樣當一個自定義組件接入的時候,只需要在外面包一層,并把 class 的命名為以 _Field 開頭的格式就可以被 Form 收集管理了。

接入組件里面需要做的就是,在合適的時機調用 handleFieldChange 方法,并把要傳遞的數據作為參數傳遞出來就可以了。

為什么一定要執迷不悟的使用遍歷這種低效的方式去收集呢,其實都是為了組件上使用的方便。這樣就不需要每次在引用的時候在對子表單做什么操作了。

初始化 Form state

上一步拿到了所有的子表單,然后通過調用 initialFormDataStructure 拿來初始化 Form 的 state.data 的結構,同時通知到外面 Form 發生了變化。

子表單數量或類型發生變化時

當 Form 下面子組件被添加或刪除時,需要及時更新 Form Data 的結構。通過調用 updateFormDataStructure
把新增的或者修改的子表單更新到最新,并通知到外面 Form 發生了變化。

子表單內部狀態發生變化時

在第一步收集子表單的時候就已經把 handleFieldChange 注入到了子表單組件里面,所以子表單來決定調用的時機。當 handleFieldChange 被調用的時候,首先對 Form state 進行更新,然后外通知子表單發生了變化,同時通知外面 Form 發生了變化。

這樣看起來整個流程就走通了,但實際上存在很多問題。

首先由于 setState 是一個異步的過程,只有在 render 后才能獲取到最新的 state. 這就導致,在一個生命周期循環內如果我多次調用了 setState ,那么兩次調用之間對 state 的讀取很可能是不準確的。(有關生命周期的詳細內容可以看這篇文章:https://www.w3ctech.com/topic...)

所以我創建了一個臨時變量 currentState 來存放當前狀態下最新的 state,每次 setState 的時候都對其進行更新。

另一個問題是當 Form 發生變化的時候,updateFormDataStructure 調用的過于頻繁。其實只有在子表單的數量或者類型發生變化時才需要更新 Form state 的結構。而直接去對比子表單的類型是否發生變化也是意見開銷很大操作,所以選擇另一種折中方式。通過給 Form 當前的狀態打標,將 Form 可能處于的狀態都標識出來:

const?STATUS?=?{
??Init:?"Init",
??Normal:?"Normal",
??FieldChange:?"FieldChange",
??UpdateFormDataStructure:?"UpdateFormDataStructure",
??Submit:?"Submit"
}

這樣,只有在 Form 的 STATUS 處于 Normal 的時候才對其進行 updateFormDataStructure 操作。這樣就可以省去很多次渲染以及無效的對外觸發的 FormChange 事件。

提交和對外暴露 Form 狀態的方法和之前基本一致,這樣整個對 Form 的重構就算完成了,具體項目中使用體驗還不錯 O(∩_∩)O

Form 組件地址: https://github.com/NE-LOAN-FED/NE-Component/tree/master/src/Form

最后,如果看文章的你有什么更好的想法,請告訴我?。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82085.html

相關文章

  • Ant Design Pro - 實踐React Hooks - 頁面

    摘要:背景目前是社區最炙手可熱的新技術,我們準備追一下熱度,在當前的項目中實踐一下技術。我們的項目使用的腳手架是,初步想法是把現有的一個有狀態頁面組件重構成函數組件。存放表單值的狀態是聲明在列表組件,傳給表單組件。 背景 React Hooks目前是React社區最炙手可熱的新技術,我們準備追一下熱度,在當前的項目中實踐一下Hooks技術。 我們的項目使用的腳手架是Ant Design P...

    wangbjun 評論0 收藏0
  • 更快助你弄懂React-高階組件

    摘要:等價于是一個返回函數的函數就是個高階函數返回的函數就是一個高階組件,該高階組件返回一個與關聯起來的新組件的也是一樣的總結一下高階組件是對代碼進行更高層次重構的好方法,如果你想精簡你的和生命周期方法,那么高階組件可以幫助你提取出可重用的函數。 談到react,我們第一個想到的應該是組件,在react的眼中可真的是萬物皆組件。就連我們獲取數據用到的axios也可以用組件來表示...比如,我...

    qylost 評論0 收藏0
  • 更快助你弄懂React-高階組件

    摘要:等價于是一個返回函數的函數就是個高階函數返回的函數就是一個高階組件,該高階組件返回一個與關聯起來的新組件的也是一樣的總結一下高階組件是對代碼進行更高層次重構的好方法,如果你想精簡你的和生命周期方法,那么高階組件可以幫助你提取出可重用的函數。 談到react,我們第一個想到的應該是組件,在react的眼中可真的是萬物皆組件。就連我們獲取數據用到的axios也可以用組件來表示...比如,我...

    libxd 評論0 收藏0
  • 更快助你弄懂React-高階組件

    摘要:等價于是一個返回函數的函數就是個高階函數返回的函數就是一個高階組件,該高階組件返回一個與關聯起來的新組件的也是一樣的總結一下高階組件是對代碼進行更高層次重構的好方法,如果你想精簡你的和生命周期方法,那么高階組件可以幫助你提取出可重用的函數。 談到react,我們第一個想到的應該是組件,在react的眼中可真的是萬物皆組件。就連我們獲取數據用到的axios也可以用組件來表示...比如,我...

    dreamGong 評論0 收藏0
  • 更快助你弄懂React-高階組件

    摘要:等價于是一個返回函數的函數就是個高階函數返回的函數就是一個高階組件,該高階組件返回一個與關聯起來的新組件的也是一樣的總結一下高階組件是對代碼進行更高層次重構的好方法,如果你想精簡你的和生命周期方法,那么高階組件可以幫助你提取出可重用的函數。 談到react,我們第一個想到的應該是組件,在react的眼中可真的是萬物皆組件。就連我們獲取數據用到的axios也可以用組件來表示...比如,我...

    xushaojieaaa 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<