优雅地提高 React 的表单页面的开发效率
Den Form
為什么叫 Den Form ? 可能是因為 丹鳳眼 非常迷人吧...
一個非常輕巧的 Form 實現, gzip 體積只有 3kb, 可以很方便跨越組件層級獲取表單對象, 或者管理聯動更新
安裝
$ yarn add react-den-form 復制代碼基礎使用
Form 組件會在有 field 屬性的子組件上注入 onChange 事件, 并獲取其中的值
field 屬性是 Form 組件用于標記哪一類子組件需要被監管的字段, 并且也是用于校驗更新的 key
field 可以重復, 但是如果兩個子組件擁有相同的 field 值, 那么當此類 field 需要更新時, 兩個子組件都會進行更新
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 方法打印如下:
{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 默認處理了表單嵌套.
由于 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>); }; 復制代碼跨組件的值獲取
當我們輸入數據時, onChange 方法打印如下:
{userName: "333", password: "555", subPassword: "666"} 復制代碼自定義 Field 組件
如果我們自己定義的特殊組件, 需要滿足兩個條件:
: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)
異步請求提交
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><inputfield="userName"errorcheck={/123/}errorstyle={{ color: "#f00" }}/></Form></div>); }; 復制代碼表單校驗相關的 api:
| 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)}><inputfield="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><FormonChange={({ 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 內部更新時只會針對指定的子組件進行更新.
如果因為使用 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 |
子組件關聯參數
一個子組件可以被識別關聯的參數如下
<inputfield="userName"submittype="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 表單相關的痛點 :)
轉載于:https://juejin.im/post/5c977e075188252d785f20b5
總結
以上是生活随笔為你收集整理的优雅地提高 React 的表单页面的开发效率的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 说说 Spring 事务管理的实现类
- 下一篇: Python学习之路:NumPy初识