国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > JS > 正文

React 使用Hooks簡(jiǎn)化受控組件的狀態(tài)綁定

2024-05-06 16:49:10
字體:
供稿:網(wǎng)友

開始之前

閱讀本文需要對(duì)以下幾項(xiàng)有一定了解

ECMAScript 6

文章中大量用到了 ES6 語法,比如解構(gòu)賦值和函數(shù)參數(shù)默認(rèn)值剩余參數(shù)展開語法箭頭函數(shù)等。

Hooks

React 在 16.8 版本中推出了 Hooks,它允許你在“函數(shù)組件”中使用“類組件”的一些特性。

React 本身提供了一些 Hooks,比如 useState、useReducer 等。通過在一個(gè)以“use”作為命名起始的函數(shù)中調(diào)用這些 Hooks,就得到了一個(gè) custom Hook(自定義 Hook)。

Custom Hooks 允許我們把任何邏輯封裝到其中,以便于復(fù)用足夠小的組件邏輯。

Controlled Components

當(dāng)我們把像 <input> <textarea> 和 <select> 這樣的 HTML 元素本身的狀態(tài)交給 React state 去管理,我們就得到了一個(gè)“受控組件”。

styled-components

一個(gè)與 React 契合良好的 CSS in JS 庫(kù)。它允許你使用 JS 編寫樣式,并編譯成純 CSS 文件。
下面代碼中所有的樣式都是使用它編寫的。如果對(duì)代碼中樣式的實(shí)現(xiàn)不是很感興趣的話, 這個(gè)可以跳過。

代碼實(shí)現(xiàn)

Input 組件

首先我們需要實(shí)現(xiàn)一個(gè) Input 組件,我們將在該組件的基礎(chǔ)上進(jìn)行輸入、校驗(yàn)并提示。

Input.js

import React from 'react';import PropTypes from 'prop-types';import styled from 'styled-components';const Wrap = styled.div({ display: 'flex', flexDirection: 'column', label: { display: 'flex', alignItems: 'center' }, input: {  marginLeft: 8, }, p: {  color: 'red', },});function Input({ label, type, helperText, error, ...otherProps }) { return (  <Wrap>   <label>    {label}:    <input {...otherProps} type={type} />   </label>   {error && <p>{helperText}</p>}  </Wrap> );}Input.propTypes = { label: PropTypes.string, type: PropTypes.string, helperText: PropTypes.string, error: PropTypes.bool,};export default Input;

該組件主要接收以下幾個(gè) props:

  • label label 標(biāo)簽的文本
  • type 賦值給原生 input 標(biāo)簽的 type 屬性
  • error 數(shù)據(jù)類型為 Boolean,如果為 true 則表示當(dāng)前表單域有錯(cuò)誤,即驗(yàn)證不通過
  • helperText 當(dāng)前表單域驗(yàn)證不通過時(shí),顯示在表單域下方的提示文字
  • otherProps props 中除了上述四個(gè)以外的其他屬性,全部賦值給原生 input 標(biāo)簽

Custom Hook

有了 UI 組件之后,就可以開始實(shí)現(xiàn)我們的自定義 Hook 了。

useInput.js

import { useState } from 'react';export default function useInput({ initValue = '', helperText = '', validator = () => true, validateTriggers = ['onChange'],} = {}) { // 保存用戶輸入的值,使用 initValue 作為初始值 const [value, setValue] = useState(initValue); // Boolean 類型,表示當(dāng)前表單項(xiàng)的驗(yàn)證狀態(tài) const [error, setError] = useState(false); function onChange(e) {  const { value } = e.target;  setValue(value);  // 根據(jù) validateTriggers 的選項(xiàng),決定是否要在 onChange 里進(jìn)行校驗(yàn)  if (validateTriggers.includes('onChange')) {   setError(!validator(value));  } } /**  * 根據(jù) validateTriggers 生成相應(yīng)的事件處理器  */ function createEventHandlers() {  const eventHandlers = {};  validateTriggers.forEach(item => {   // 生成相應(yīng)的事件處理器,并在其中做輸入校驗(yàn)。   eventHandlers[item] = e => {    const { value } = e.target;    setError(!validator(value));   };  });  return eventHandlers; } const eventHandlers = createEventHandlers(); return {  value,  helperText,  error,  ...eventHandlers,  onChange, };}

useInput 接收一個(gè) options 對(duì)象作為參數(shù),考慮到擴(kuò)展性,使用一個(gè)配置對(duì)象作為參數(shù)比較好。

options 對(duì)象擁有以下幾個(gè)屬性:

  • initValue 輸入框的初始值
  • helperText 當(dāng)表單驗(yàn)證不通過時(shí)顯示的字符串
  • validator 用于進(jìn)行表單驗(yàn)證的函數(shù),接收 value 作為參數(shù),并返回一個(gè) Boolean 值,表示表單驗(yàn)證是否通過
  • validateTriggers 字符串?dāng)?shù)組,表明在哪個(gè)或哪幾個(gè)事件中調(diào)用 validator 進(jìn)行輸入校驗(yàn)。

在函數(shù)體中,我們調(diào)用兩次 useState 來初始化 value 和 error 的值,分別保存用戶輸入的值和當(dāng)前表單域的校驗(yàn)結(jié)果。
然后,聲明一個(gè) onChange 方法用來綁定 input 元素的 change 事件,在該方法中,我們把用戶輸入的值賦值給 value,同時(shí)根據(jù) validateTriggers 的值,決定是否要在該方法中進(jìn)行輸入校驗(yàn)。該方法隨后會(huì)被返回出去,再作為 props 傳遞給相應(yīng)的組件,完成受控組件的狀態(tài)綁定。

我們還需要聲明一個(gè) createEventHandlers 方法,該方法通過遍歷 validateTriggers,生成相應(yīng)的事件處理器,并在這些事件處理器中進(jìn)行輸入校驗(yàn)。

最后我們調(diào)用 createEventHandlers 方法,并把生成的 eventHandlers(事件處理器) 通過擴(kuò)展運(yùn)算符,插入到最終返回的對(duì)象中。

注意:這里我們需要把 onChange 放在最后,以免帶有狀態(tài)綁定的 onChange 方法被 eventHandlers 中的 onChange 覆蓋掉。

具體使用

現(xiàn)在讓我們來看看實(shí)際該如何使用:

import React from 'react';import Input from './Input';import useInput from './useInput';// 用于驗(yàn)證郵箱的正則表達(dá)式const EMAIL_REG = //S+@/S+/./S+/;export default function Form() { const email = useInput({  initValue: '',  helperText: '請(qǐng)輸入有效的郵箱!',  validator: value => EMAIL_REG.test(value),  validateTriggers: ['onBlur'], }); const password = useInput({  initValue: '',  helperText: '密碼長(zhǎng)度需要在 6-20 之間!',  validator: value => value.length >= 6 && value.length <= 20,  validateTriggers: ['onChange', 'onBlur'], }); /**  * 判斷是否禁用按鈕  */ function isButtonDisabled() {  // 當(dāng)郵箱或密碼未填寫時(shí),或者郵箱或密碼輸入校驗(yàn)未通過時(shí),禁用按鈕  return !email.value || !password.value || email.error || password.error; } /**  * 處理表單提交  */ function handleButtonClick() {  console.log('郵箱:', email.value);  console.log('密碼:', password.value); } return (  <div>   <Input {...email} label="郵箱" type="email" />   <Input {...password} label="密碼" type="password" />   <button disabled={isButtonDisabled()} onClick={handleButtonClick}>    登錄   </button>  </div> );}

這里調(diào)用了兩次 useInput,初始化 email 和 password 表單域數(shù)據(jù)。

然后使用擴(kuò)展運(yùn)算符,把值全部賦給 Input 組件。只用了幾行代碼就完成了定義初始值和受控組件的綁定,是不是很方便?

當(dāng)我們輸入郵箱的時(shí)候,并不會(huì)出現(xiàn)校驗(yàn)提示,但是一旦從郵箱輸入框失去焦點(diǎn)以后,輸入的值就會(huì)被校驗(yàn),并根據(jù)校驗(yàn)結(jié)果顯示相應(yīng)的提示。而密碼輸入框,則會(huì)在輸入的過程中和失焦后都進(jìn)行校驗(yàn)。

總結(jié)

上面這個(gè)例子已經(jīng)可以處理基本的表單驗(yàn)證,至于格式化用戶輸入的數(shù)據(jù)以及自定義收集表單域的值的時(shí)機(jī)等其他需求,我就不再演示了,大家可以自行設(shè)計(jì)。這也是 Hooks 的特殊之處,它讓我們可以更容易的復(fù)用邏輯代碼,可以根據(jù)需要自行編寫 custom Hooks。

文章中關(guān)于 useInput 的 API 設(shè)計(jì)只是眾多方案中的一種,只是為大家提供一些參考。你也可以把整個(gè)表單的狀態(tài)封裝到一個(gè) useForm 方法中,統(tǒng)一管理所有表單域的狀態(tài)。

希望本文能為大家?guī)硪恍╆P(guān)于如何使用 Hooks 的靈感,即使從來沒有使用過 Hooks,也強(qiáng)烈建議大家嘗試一下。我已經(jīng)在項(xiàng)目中大量使用 Hooks 了,并且它也為我?guī)砹撕芎玫男Ч?/p>

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JavaScript/Ajax教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 凭祥市| 平昌县| 敖汉旗| 色达县| 盘锦市| 祁门县| 梁山县| 无棣县| 兴安县| 库伦旗| 沈阳市| 文安县| 进贤县| 孙吴县| 察哈| 兴山县| 沽源县| 广饶县| 庄浪县| 定兴县| 霞浦县| 溆浦县| 遵义市| 泾源县| 怀来县| 海晏县| 石柱| 金塔县| 武鸣县| 麻城市| 高清| 庄浪县| 伊川县| 唐山市| 铁力市| 前郭尔| 阿拉善右旗| 大足县| 天长市| 贵阳市| 福泉市|