優雅地提升 React 的表單頁面的開發效率

Den Form

爲何叫 Den Form ? 多是由於 丹鳳眼 很是迷人吧...css

一個很是輕巧的 Form 實現, gzip 體積只有 3kb, 能夠很方便跨越組件層級獲取表單對象, 或者管理聯動更新react

安裝

$ yarn add react-den-form 
複製代碼

基礎使用

Form 組件會在有 field 屬性的子組件上注入 onChange 事件, 並獲取其中的值json

field 屬性是 Form 組件用於標記哪一類子組件須要被監管的字段, 而且也是用於校驗更新的 keyapi

field 能夠重複, 可是若是兩個子組件擁有相同的 field 值, 那麼當此類 field 須要更新時, 兩個子組件都會進行更新bash

import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div> <Form onSubmit={({ data }) => console.log(data)}> <input field="userName" /> </Form> </div> ); }; 複製代碼

當咱們輸入數據後, 按回車鍵, onSubmit 方法打印以下:app

{userName: "333"}
複製代碼

層級獲取數據不須要進行額外處理

import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div>
      <Form onChange={({ data }) => console.log(data)}>
        <div>
          <div>
            <div>
              <input field="userName" />
            </div>
            <input field="password" />
          </div>
        </div>
      </Form>
    </div>
  );
};
複製代碼

當咱們輸入數據時, onChange 方法打印以下:異步

{userName: "333", password: "555"}
複製代碼

Form 表單嵌套不須要處理

有時候, 咱們會有一些頁面結構讓兩個不一樣的表單進行嵌套, 如登陸時, 驗證碼的輸入框在用戶名和密碼中間, 而驗證碼有單獨的請求. 固然, 咱們能夠更換實現方式, 可是對於這類場景, DenForm 默認處理了表單嵌套.ide

因爲 Form 內部有一個 form 標籤, 外層 onSubmit 會捕獲全部子組件的 onSubmit 事件, 可是 data 數據只會捕獲 當前層級內的 field 對象函數

export default () => {
  return (
    <div>
      {/* 此 Form 只會捕獲 userName及password, code被子Form攔截了 */}
      <Form onSubmit={({ data }) => console.log("1", data)}>
        <input field="userName" />
        {/* 此 Form 只會捕獲 age */}
        <Form onSubmit={({ data }) => console.log("2", data)}>
          <input field="code" />
          <button type="submit">此Submit會被最近的父級捕獲</button>
        </Form>
        <input field="password" />
      </Form>
    </div>
  );
};
複製代碼

跨組件的值獲取

  1. 爲對象標記一個 toform 屬性, 會爲對象注入一個 ToForm 組件
  2. 而後使用 ToForm 組件在對象內部對錶單進行歸納
import React from "react";
import Form from "react-den-form";

// 此對象會被注入 ToForm 組件
function SubInput({ ToForm }) {
  return (
    <div>
      <div>
      <ToForm>
        <input field="subPassword" />
      </ToForm>
      </div>
    </div>
  );
}

export default () => {
  return (
    <div>
      <Form onChange={({ data }) => console.log(data)}>
        <div>
          <div>
            <div>
              <input field="userName" />
            </div>
            <input field="password" />
            <SubInput toform />
          </div>
        </div>
      </Form>
    </div>
  );
};
複製代碼

當咱們輸入數據時, onChange 方法打印以下:post

{userName: "333", password: "555", subPassword: "666"}
複製代碼

自定義 Field 組件

若是咱們本身定義的特殊組件, 須要知足兩個條件:

  1. 組件外部的 props 須要設置 field 屬性
  2. 組件內部須要使用 this.props.onChange 返回數據
import React from "react";
import Form from "react-den-form";

class SubInput extends React.Component {
  handleOnChange = e => {
    // 須要使用 this.props.onChange 返回數據
    this.props.onChange(e.target.value);
  };

  render() {
    return <input onChange={this.handleOnChange} />;
  }
}

export default () => {
  return (
    <div>
      <Form onChange={({ data }) => console.log(data)}>
        {/* 須要設置 field 屬性 */}
        <SubInput field="userName" />
      </Form>
    </div>
  );
};
複製代碼

:art: 使用 onChangeGetter 獲取自定義組件的值

如下標籤, From 會自動識別 onChange 的返回值, 進行解析獲取

import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div>
      <Form onChange={(...args) => console.log(args)}>
        <input field="userName" />
        <textarea field="password" />
        <select field="loginType">
          <option value="signUp">Sign up</option>
          <option value="signIn">Sign in</option>
        </select>
      </Form>
    </div>
  );
};
複製代碼

咱們本身定義的特殊組件, 若是它們的 onChange 的返回值結構不肯定, 咱們能夠編寫 onChangeGetter 屬性:

import React from "react";
import Form from "react-den-form";

class SubInput extends React.Component {
  // 假定數據有必定的層級
  inputData = {
    value: ""
  };

  handleOnChange = e => {
    this.inputData.value = e.target.value;
    this.props.onChange(this.inputData);
  };

  render() {
    return <input onChange={this.handleOnChange} />;
  }
}

export default () => {
  return (
    <div>
      <Form onChange={({ data }) => console.log(data)}>
        <SubInput field="userName" onChangeGetter={e => e.value} />
      </Form>
    </div>
  );
};
複製代碼

onChangeGetter 的默認值至關於 onChangeGetter={e => e}

表單提交

如下三個情形爲都會觸發 Form 的 onSubmit 函數:

  • 包含 field 屬性的對象中, 使用鍵盤的回車鍵
  • 包含 submit 屬性, 點擊(onClick)
  • 包含 type="submit" 屬性的對象中, 點擊(onClick)
import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div>
      <Form onSubmit={({ data }) => console.log(data)}>
        <input field="userName" />
        <input submit field="password" />
        <button type="submit" />
      </Form>
    </div>
  );
};
複製代碼

異步請求提交

Form 表單內部並沒有封裝請求行爲, 請在 onSubmit 事件中自行處理, 如:

import React from "react";
import Form from "react-den-form";

function fetchLogin({ data }) {
  fetch("/api/login", { method: "post", body: JSON.stringify(data) })
    .then(res => {
      return res.json();
    })
    .then(data => {
      console.log(data);
    });
}

export default () => {
  return (
    <div>
      <Form onSubmit={fetchLogin}>
        <input field="userName" />
        <input field="password" />
      </Form>
    </div>
  );
};
複製代碼

上下文獲取數據

咱們爲 Form 顯式注入一個 data, 當數據變化時, data 的值也會變化, 這樣能夠在上下文獲取 Form 的數據

// React.Component 版本
import React from "react";
import Form from "react-den-form";

export default class extends React.Component {
  data = {};

  render() {
    return (
      <div> <Form data={this.data}> <input field="userName" /> <button onClick={() => console.log(this.data)}>show-data</button> </Form> </div> ); } } 複製代碼
// useHooks 版本
import React, { useState } from "react";
import Form from "react-den-form";

export default () => {
  const [data] = useState({});

  return (
    <div> <Form data={data}> <input field="userName" /> <button onClick={() => console.log(data)}>show-data</button> </Form> </div> ); }; 複製代碼

輸入數據, 點擊 button, data 數據打印以下:

{ userName: "dog" }
複製代碼

表單校驗

表單校驗是無痛的, 而且是高效的

咱們給 input 組件添加 errorcheck 屬性, 該屬性能夠是一個正則對象, 也能夠是一個  函數, 若是 errorcheck 校驗的結果爲 false, 就會將其餘 error 相關的屬性賦予至組件中

以下代碼, 若是 input 內容不包含 123, 字體顏色爲紅色:

import "./App.css";
import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div> <Form> <input field="userName" errorcheck={/123/} errorstyle={{ color: "#f00" }} /> </Form> </div> ); }; 複製代碼

表單校驗相關的 api:

prop 類型 用途
errorcheck 正則或函數 若返回值爲 false, 將其餘 error Api 應用至組件中
errorstyle style 對象 若校驗爲錯誤, 將 errorstyle 合併至 style 中
errorclass className 字符串 若校驗爲錯誤, 將 errorstyle 合併至 className 中
errorprops props 對象 若校驗爲錯誤, 將 errorprops 合併至整個 props 中

爲何是 errorcheck 而不是 errorCheck ? 這是由於 React 對 DOM 元素屬性的定義爲 lowercass:

Warning: React does not recognize the `errorCheck` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `errorcheck` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
複製代碼

表單校驗時進行特殊處理

若是咱們有一個需求, 當表單校驗錯誤時, 顯示一個提示信息, 當表單校驗經過時, 取消提示信息, 咱們就須要對每次校驗有差別時, 進行處理

使用 onChange 方法每次都會被執行, 但是咱們只但願在表單校驗結果有變化時進行提示

Form 提供了一個 onErrorCheck 的屬性, 知足以上需求

import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div> {/* 只有當 input內容校驗結果發生變化時, onErrorCheck 纔會執行 */} <Form onErrorCheck={({ isError, data }) => console.log(isError, data)}> <input field="userName" errorcheck={/123/} errorstyle={{ color: "#f00" }} /> </Form> </div> ); }; 複製代碼

聯動

當咱們修改一個對象時, 根據某些條件, 但願修改另外一個對象的行爲咱們稱之爲聯動

DenForm 的聯動是高性能的, 僅更新須要更新的對象

咱們能夠在任何 Form 的回調函數中使用 update 進行更新某個被 field 捆綁的組件的 value 或者 props

下面這個例子: 1. 當在 password 輸入時, 會將 userName 的 input 框內容改成 'new value'; 2. 當 userName 的 input 的內容包含 'aa' 時, 會將 password 的 value 和 style 進行修改;

import React from "react";
import Form from "react-den-form";

export default () => {
  return (
    <div>
      <Form
        onChange={({ data, field, update }) => {
          if (field === "password") {
            update({ userName: "new value" });
          }
          if (/aa/.test(data.userName)) {
            update({
              password: {
                value: "new value and style",
                style: { fontSize: 30 }
              }
            });
          }
        }}
      >
        <input field="userName" />
        <input field="password" />
      </Form>
    </div>
  );
};
複製代碼

性能開銷

Form 存在的意義在於簡化開發, 用計算機的時間換取開發者的時間, 因此會有一些性能開銷.

可是 Form 的開銷絕對不大, 由於 Form 內部更新時只會針對指定的子組件進行更新.

  1. 每一個包含 field 屬性的子組件都至關於一個受控組件, 當子組件 onChange 時, 此子組件會進行更新
  2. Form 組件聲明或被外部更新時會去查詢當前 JSX 對象中的全部子組件是否包含 field 或者 submit 屬性, 若是包含, 則注入 onChange 或 onClick; 若是不但願 Form 被外部更新, 請聲明 <Form shouldUpdate={false} >{...}</Form>

若是由於使用 Form 遇到了性能問題, 請檢查如下狀況:

  • 請減小 Form 內部子組件的個數, 最好不要超過 100 個
  • 在一個無限長的滾動列表外包裹 Form 時, 請儘可能使用 react-virtualized 或 react-window 類型的虛擬 List 組件, 以減小 Form 包裹的內容個數
  • 若是 Form 子組件的個數過多時, 請確保 Form 組件不會由外部頻繁更新, 或者添加 shouldUpdate={false} 至 Form 中: <Form shouldUpdate={false} >{...}</Form>

咱們有理由相信, 在一個設計合理的應用中, 每一個 Form 包裹的組件個數應該是有限的

支持哪些 React 渲染層 ?

此庫支持全部 React 的渲染層, 如 ReactDOM, ReactNative, ReactVR, 可是非 ReactDOM 中, 須要初始化事件類型

如 ReactNative 中, 在項目之初設定:

import { immitProps } from "react-den-form";

// 設定 ReactNative 中的讀取值和更新值的監聽屬性:
immitProps.value = "value";
immitProps.change = "onChange";
immitProps.click = "onPress";
複製代碼

API

Form API

屬性 描述 類型 默認值 必填
data 用於捆綁上下文的 data 對象, 也會當成默認值設置到相應的 field 監管的組件中 Object: {:} {} --
onChange 當有 field 監管的子組件執行 onChange 時, 進行回調 (IEvents) => void undefined --
onSubmit 當有表單進行提交時, 進行回調 (IEvents) => void undefined --
onErrorCheck 當有 field 監管的子組件的錯誤校驗結果發生變化時, 進行回調 (IEvents) => void undefined --

IEvents API (回調函數的參數)

Form 組件回調函數的參數以下: ({isError, event, data, field, value, element, update }) => void

具體的 API 描述以下:

屬性 描述 類型 默認值 必傳
isError 最後輸入的對象校驗是否正確 Boolean false true
event onChange 的原始返回對象 Boolean undefined false
data form.data 對象, 包含全部 field 監管的值 Object: {:} {} true
field 當前修改的組件的 field 屬性 String undefined true
value 當前修改的值 Boolean '' String or Number
element 當前修改的 ReactElement Boolean React.Element true
update 更新某個 field 監管的對象 (IEvents)=> {:} (IEvents)=> {:} true

子組件關聯參數

一個子組件能夠被識別關聯的參數以下

<input
  field="userName"
  submit
  type="submit"
  errorcheck={/123/}
  errorstyle={{ color: "#f00" }}
  errorclass="input-error-style"
  errorprops={{ disable: true }}
/>
複製代碼

具體的 API 描述以下:

屬性 描述 類型 默認值 必傳
field 用於標記組件是否被 Form 組件監聽, 而且也是 data 用於存放值的 key String undefined
submit 用於肯定當前對象是否能夠響應 onClick 事件進行提交 Boolean undefined --
toform 注入ToForm 組件, 讓子組件對象也能夠歸納子表單 Boolean undefined
type 當 type = "submit" 時, 能夠響應 onClick 事件進行提交 Object: {:} undefined --
errorcheck 用於校驗當前對象 value 是否錯誤 正則對象或函數 undefined --
errorstyle 當前對象 value 錯誤時, 合併 errorstyle 至 style Object undefined --
errorclass 當前對象 value 錯誤時, 合併 errorclass 至 className String undefined --
errorprops 當前對象 value 錯誤時, 合併 errorprops 至 props Object undefined --

以上就是所有, 但願 Den Form 可以幫到你解決 React 表單相關的痛點 :)

相關文章
相關標籤/搜索