HTML5 및 검증을 사용하여 React에서 빠른 양식 만들기
85679 단어 reacthtmlformsjavascript
우리가 폼 처리를 간소화할 수 있는 방법 중 하나는 데이터와 함수로 폼을 표시하는 것이다.이런 표시를 통해 우리는 폼 요소를 만들고, 읽고, 업데이트할 수 있다.
먼저 양식에 참여하거나 사용할 수 있는 가능한 양식 요소 목록을 살펴보겠습니다.
text
에 대한 JS 객체 표현을 작성해 보겠습니다.const fields = [
{
type: "text", // input type
name: "fullName", // Form input name
label: "Full Name", // Label for Input
placeholder: "John Doe" // Placeholder
}
]
React에서 다음과 같은 입력 유형의 텍스트를 만듭니다.// Input.js
import React from "react";
export function Input({ field }) {
const id = `input-id-${+Date.now()}-${Math.random()}`
return (
<div className="form-field">
<label htmlFor={id}>{field.label}</label>
<input
id={id}
type={field.type}
name={field.name}
placeholder={field.placeholder}
/>
</div>
)
}
🤔 But what about events? We need values from the Form!
FormData API를 사용하여 양식에서 값을 수집합니다.
🤨 But we still need events to validate the Values!
제출을 방지하기 위해 HTML5의 원본 폼 검증을 사용할 것입니다.
하지만 고급 검증이 필요하다면.값을 조작하고 변경하기 위해 프로세서를 추가한 다음 서버에 보낼 수 있습니다
const fields = [
{
type: 'text', // input type
name: 'fullName', // Form input name
label: 'Full Name', // Label for Input
placeholder: 'John Doe', // Placeholder
required: true
}
]
위의 필드 정의를 통해 다른 입력을 만들 수 있습니다.그러나 다른 텍스트 기반 입력의 경우 다음과 같이 입력 어셈블리가 표시됩니다.// Input.js
import React, {useRef} from 'react'
import TextInputRenderer from './InputType/Text'
const getRenderer = (type) => {
switch(type.toLowerCase()) {
case 'tel':
case 'url':
case 'text':
case 'date':
case 'time':
case 'file':
case 'week':
case 'month':
case 'image':
case 'email':
case 'color':
case 'range':
case 'number':
case 'search':
case 'password':
return TextInputRenderer
default: return 'div'
}
}
const Input = ({ field = {} }) => {
const inputRef = useRef(null)
const Component = getRenderer(field.type)
return (
<div className="form-field">
<Component
{...field}
ref={inputRef}
onChange={() => console.log(inputRef.current)}
/>
</div>
)
}
export default Input
// components/InputType/Text.js
import React, {Fragment} from 'react'
export default React.forwardRef((props, ref) => {
const id = `input-id-${+Date.now()}-${Math.random()}`
return (
<Fragment>
<label htmlFor={id}>{props.label}</label>
<input id={id} {...props} ref={ref} />
</Fragment>
)
})
위 코드에서 우리는 텍스트의 입력을 바탕으로 추출할 것이다components/InputType/Text.js
.Input
구성 요소는 필요한 참조 포인트 및 프로세서만 연결합니다.여기서 주의해야 할 것은 React
forwardRef
로 ref
를 도구로 서브어셈블리에 전달한다.계속하기 전에, 우리는 독특한 ID를 사용하여 논리를 생성하고 있음을 알 수 있다.우리는 이 논리를 별도의 효용 함수로 추출할 수 있다.
// src/utils/get-id.js
export default () => [
'input',
+Date.now(),
Math.random()
].join('-')
앞으로 우리는 건장한 UUID와 uuid 같은 라이브러리를 함께 사용할 수 있다이제 라디오, 선택, 체크 상자를 입력하면 다른 표시로 표시됩니다.
확인란
일반적으로 단수 체크 상자는 텍스트 입력으로 표시됩니다.복선상자 목록에 흔히 볼 수 있는 용례가 있을 수 있지만, 이것은 우리가 구성 요소를 바꾸어야 하는 부분이다
import React, {Fragment} from 'react'
import getId from '../../utils/get-id'
export default React.forwardRef((props, ref) => {
const id = getId()
return (
<Fragment>
<label htmlFor={id}>{props.label}</label>
{props.options ? (
<span className="flex-col">
{props.options.map(item => {
const id = getId()
return (
<span key={id}>
<input id={id} {...props} value={item.value} />
<label htmlFor={id}>{item.label}</label>
</span>
)
})}
</span>
) : <input id={id} {...props} ref={ref} />}
</Fragment>
)
})
라디오
무선조의 경우 모든 입력은 같은 값
name
을 가진다.그리고 필드 정의는 선택 단추 목록을 만들기 위해 선택 목록을 받아들여야 합니다.import React, {Fragment} from 'react'
import getId from '../../utils/get-id'
export default React.forwardRef(({options, label, ...props}, ref) => (
<Fragment>
<label>{label}</label>
<span className="flex-col">
{options.map(item => {
const id = getId()
return (
<span key={id}>
<input id={id} {...props} value={item.value} />
<label htmlFor={id}>{item.label}</label>
</span>
)
})}
</span>
</Fragment>
))
고르다
옵션이 있고 기본 렌더링에서는 다른 옵션을 선택합니다.따라서, 우리는 select를 위해 다른 구성 요소를 만들어야 한다.
import React, {Fragment} from 'react'
import getId from '../../utils/get-id'
export default React.forwardRef(({options, ...props}, ref) => {
const id = getId()
return (
<Fragment>
<label htmlFor={id}>{props.label}</label>
<select ref={ref} {...props}>
{options.map(item => (
<option key={item.value} value={item.value} selected={item.selected}>
{item.label}
</option>
))}
</select>
</Fragment>
)
})
현재 우리의 for가 설정되어 있습니다. 폼에서 검증을 처리해야 합니다.유효성 검사(예:
required
필드min
및 max
maxLength
및 minLength
pattern
email
url
export default [
{
type: 'text', // input type
name: 'fullName', // Form input name
label: 'Full Name', // Label for Input
placeholder: 'John Doe', // Placeholder
pattern: '[A-Za-z\\s]{1,}',
required: true
},
{
type: 'date', // input type
name: 'dob', // Form input name
label: 'Date of Birth', // Label for Input
required: true
},
{
type: 'number', // input type
name: 'workCapacity', // Form input name
label: 'Weekly Work Capacity', // Label for Input
required: true,
min: 10,
max: 8*7, // 8 hrs per day for 7 days of week
step: 4 // half day steps
},
{
type: 'file', // input type
name: 'profilePicture', // Form input name
label: 'Profile Picture', // Label for Input
required: true
},
{
type: 'radio',
name: 'gender',
label: 'Gender',
required: true,
options: [
{
label: 'Male',
value: 'M'
}, {
label: 'Female',
value: 'F'
}, {
label: 'Other',
value: 'O'
}, {
label: 'I\'d rather not specify',
value: '-'
},
]
},
{
type: 'checkbox',
name: 'foodChoices',
label: 'Food Choices',
options: [
{
label: 'Vegan',
value: 'V+'
}, {
label: 'Vegetarian',
value: 'V'
}, {
label: 'Non Vegetarian',
value: 'N'
}, {
label: 'I\'d rather not specify',
value: '-'
},
]
},
{
type: 'select',
name: 'primaryLanguage',
label: 'Primary Language',
required: true,
options: [
{
label: 'English (US)',
value: 'en_US'
}, {
label: 'English (UK)',
value: 'en_UK'
}, {
label: 'Deutsch',
value: 'de_DE'
}, {
label: 'French',
value: 'fr_FR'
}
]
},
{
type: 'email',
name: 'email',
label: 'Your Email',
required: true
},
{
type: 'tel',
name: 'phoneNumber',
label: 'Your Phone Number',
required: false,
pattern: '[+0-9]{8,12}'
},
{
type: 'url',
name: 'homepage',
label: 'Your Website',
required: false
},
{
type: 'password',
name: 'password',
label: 'Your Password',
required: true
},
{
type: 'password',
name: 'confirmPassword',
label: 'Confirm Password',
required: true
},
{
type: 'checkbox',
name: 'terms',
label: '',
required: true,
options: [{
value: 'yes',
label: 'Terms and Conditions'
}]
}
]
이것은 HTML 양식의 필드를 제공합니다.HTML5가 검증됨에 따라 폼이 완전히 채워질 때까지 오류가 발생합니다.
비록 여전히 약간의 검증이 필요하지만.속성이 있는 HTML5 검증에서는 이 기능을 사용할 수 없습니다.
이를 위해서는 HTML5 인증 API를 통해 사용자 정의 인증이 필요합니다.
HTML5의 검증 API는 매우 복잡한 속성과 방법을 제공하여 HTML5의 검증 기능을 이용한다.
우선 ValidationState 인터페이스입니다.ValidationState 인터페이스는 가져온 요소의 유효성 검사 속성과 관련된 부울 상태를 제공합니다.예를 들면 다음과 같습니다.
valueMissing
네required
의 브리 답안입니다.tooLong
용maxLength
tooShort
용minLength
rangeOverflow
용max
rangeUnderflow
용min
patternMismatch
용pattern
stepMismatch
일치 또는 정제할 수 있는 값step
값이 입력 유형과 다르면typeMismatch
및 url
만 해당email
유효한 값을 입력하고 모든 유효성 검사를 통과valid
사용자 정의 오류가 설정된 경우customError
과 setCustomValidity
을 통해우리는 이러한 방법을 사용하여 사용자 정의 검증을 보고할 것이다.입력 필드의 경우
reportValidity
입력 요소setCustomValidity
입력 요소reportValidity
설정을 통해 오류가 발생하면 customError
를 True로 설정합니다.setCustomValidity
: ...
{
type: 'checkbox',
name: 'foodChoices',
label: 'Food Choices',
options: [
...
],
+ validations: [
+ (value, name, allFormValues, form) => ([
+ Boolean(allFormValues[name]),
+ `Please select atleast one of ${name}`
+ ]),
+ (value, name, allFormValues, form) => ([
+ ['V+', 'V', 'N', '-'].includes(value),
+ `Please select only from the provided choices for ${name}`
+ ])
+ ]
},
...
우리는 일련의 검증기를 받아들일 키 foodChoices
를 도입했다.이 검증기는 유효 상태를 되돌려줍니다. 유효하지 않으면 오류가 표시됩니다.
이 검증기의 매개 변수는 따를 것이다
오류를 보고할 때, 입력 구성 요소도 변경해야 합니다.또한 즉시 검사를 실행하고 필드가 유효하면 오류를 제거해야 합니다.
먼저 확인란 렌더기의 변경 사항을 살펴보겠습니다.
// src/components/InputType/Checkbox.js
import React, {Fragment, useRef, useEffect} from 'react'
import getId from '../../utils/get-id'
export default React.forwardRef(({registerField, ...props}, ref) => {
const refs = useRef([])
refs.current = (props.options || []).map(item => useRef(null))
useEffect(() => {
registerField(props.name, props.options ? refs : ref)
}, [registerField, props.name, props.options])
const id = getId()
return (
<Fragment>
<label htmlFor={id}>{props.label}</label>
{props.options ? (
<span className="flex-col">
{props.options.map((item, index) => {
const id = getId()
return (
<span key={id}>
<input id={id} {...props} value={item.value} ref={refs.current[index]} />
<label htmlFor={id}>{item.label}</label>
</span>
)
})}
</span>
) : <input id={id} {...props} ref={ref} />}
</Fragment>
)
})
확인란을 표시하는 입력 구성 요소는 다음과 같이 변경됩니다.// src/components/Input.js
// ...
// code above here is same as before for renderers
const Input = ({
field = {},
onChange = () => {},
registerField = () => {},
}) => {
const inputRef = useRef(null)
const Component = getRenderer(field.type)
return (
<div className="form-field">
<Component
{...field}
ref={inputRef}
registerField={registerField}
onChange={(...args) => onChange(field.name, ...args)}
/>
</div>
)
}
양식 구성 요소는 위의 변경 사항을 사용하여 수동으로 확인합니다.export default function Form() {
const form = useRef(null)
const inputWithError = useRef(null)
const fieldRefs = useRef({})
const registerField = (key, ref) => {
fieldRefs.current = {...fieldRefs.current, [key]: ref}
}
const getField = (key) => {
return (
Array.isArray(fieldRefs.current[key].current)
? fieldRefs.current[key].current[0]
: fieldRefs.current[key]
).current
}
const resetError = (errorFieldKey) => {
if (errorFieldKey) {
const field = getField(errorFieldKey)
if (!field) {
return
}
field.setCustomValidity('');
field.reportValidity();
}
}
const handleChange = (key, ...args) => {
resetError(inputWithError.current)
}
const customValidations = FIELDS.reduce(
(acc, field) => field?.validations
? {...acc, [field.name]: field.validations}
: acc
, {}
)
const onSubmit = (e) => {
e.preventDefault()
if (inputWithError.current) {
resetError(inputWithError.current)
}
if (!form.current.checkValidity()) {
return false;
}
const formData = serialize(new FormData(form.current))
let error = null
// Check for custom validations
const isValid = Object.keys(customValidations).reduce((acc, key) => {
const validations = customValidations[key]
const validity = validations.reduce((prevResult, validatorFn) => {
// short circuit the validations if previous one has failed
if (!prevResult) {
return false
}
// previous one was valid, let's check for current validator and return the result
const [valid, err] = validatorFn(formData[key], key, formData, form.current)
if (!valid) {
error = err
}
return valid
}, true)
acc[key] = validity;
return acc;
}, {})
if (Object.keys(isValid).length) {
const errField = Object.keys(isValid)[0]
inputWithError.current = errField
const field = getField(errField)
if (!field) {
return
}
field.setCustomValidity(error);
field.reportValidity();
}
}
return (
<form className="form" ref={form} onSubmit={onSubmit}>
{FIELDS.map((field) => (
<Input
key={field.name}
field={field}
registerField={registerField}
onChange={handleChange}
/>
))}
<button type='submit'>Submit</button>
</form>
)
}
위의 폼 구성 요소에는 많은 내용이 있습니다. 코드 블록을 보면서 분해를 시도해 보겠습니다....
const form = useRef(null)
const inputWithError = useRef(null)
const fieldRefs = useRef({})
...
이 블록은 창의 렌더링 사이에 정보를 남기기 위해 참조를 생성하고 있습니다.가장 중요한 것은 validations
입니다.이ref는 HTML5 입력 요소의 모든ref를 수집합니다. 예를 들어 Input,select,radio,checkbox 등입니다.
및
fieldRefs
는 잘못된 lat 필드를 유지합니다....
const registerField = (key, ref) => {
fieldRefs.current = {...fieldRefs.current, [key]: ref}
}
...
상기 함수는 입력 요소를 inputWithError
집합에 등록하기 위해 렌더기에 전달됩니다....
const getField = (key) => {
return (
Array.isArray(fieldRefs.current[key].current)
? fieldRefs.current[key].current[0]
: fieldRefs.current[key]
).current
}
...
현재 이 fieldRefs
함수는 필드 이름에 따라 필드를 검색하는 데 도움을 줄 것입니다.필드에 접근할 때 논리가 필요하기 때문에 이 함수가 필요합니다.이것이 바로 한 곳에서 현장 방문을 간소화하는 것이 가장 좋은 이유다.
...
const resetError = (errorFieldKey) => {
if (errorFieldKey) {
const field = getField(errorFieldKey)
if (!field) {
return
}
field.setCustomValidity('');
field.reportValidity();
}
}
...
현재, 이 함수는 모든 필드의 오류를 쉽게 리셋할 수 있습니다....
const handleChange = (key, ...args) => {
resetError(inputWithError.current)
}
...
필드의 변경 사항에 반응하기 위해 이 함수를 렌더링기에 전달할 것입니다.현재 용례에서 우리가 필요로 하는 유일한 반응은 오류를 제거하는 것이다....
const customValidations = FIELDS.reduce(
(acc, field) => field?.validations
? {...acc, [field.name]: field.validations}
: acc
, {}
)
...
위의 블록은 실행할 사용자 정의 검증을 추적하기 위해 집합된 하위 집합을 준비할 것입니다.우리가 필요한 검증을 찾아야 할 때, 이 집합은submit 방법에서 매우 편리하다.다음은 서명된 양식 제출 처리 프로그램입니다.
...
const onSubmit = (e) => {
e.preventDefault()
...
}
...
이 제출 처리 프로그램에서, 우리는 폼 데이터가 유효한지 확인하기 위해 몇 가지 조작을 실행합니다.submit 함수의 내용을 봅시다....
const onSubmit = (e) => {
e.preventDefault()
if (inputWithError.current) {
resetError(inputWithError.current)
}
if (!form.current.checkValidity()) {
return false;
}
...
}
...
위의 코드 블록에는 두 가지가 있다.우선
getField
ref와 inputWithError.current
함수를 사용하여 사용자 정의 오류를 제거합니다.두 번째는 HTML5를 사용하여 API의
resetError
ref와 form
함수를 사용하여 표의 유효성을 검증하는 것이다...
const formData = serialize(new FormData(form.current))
let error = null
...
다음에, 우리는 폼 데이터를 하나의 대상으로 준비하고, 폼 요소의 이름을 키와 값으로 한다.우리는 checkValidity
API와 FormData
함수를 빌려 실현할 것이다.serialize
함수는 다음과 같습니다.export default function serialize (formData) {
const values = {};
for (let [key, value] of formData.entries()) {
if (values[key]) {
if ( ! (values[key] instanceof Array) ) {
values[key] = new Array(values[key]);
}
values[key].push(value);
} else {
values[key] = value;
}
}
return values;
}
FormData에 대해서는 아래 게시물에서 설명했습니다.위의 serialize
기능도 위의 게시물에서 대여한 것입니다.FormData API: Handle Forms like Boss 😎 - Time to Hack
데이터를 서열화한 후에 우리는 검증을 실행해야 한다.양식 제출 방법의 다음 코드 블록에서 수행됩니다.
...
// Check for custom validations
const isValid = Object.keys(customValidations).reduce((acc, key) => {
const validations = customValidations[key]
const validity = validations.reduce((prevResult, validatorFn) => {
// short circuit the validations if previous one has failed
if (!prevResult) {
return false
}
// previous one was valid, let's check for current validator and return the result
const [valid, err] = validatorFn(formData[key], key, formData, form.current)
if (!valid) {
error = err
}
return valid
}, true)
acc[key] = validity;
return acc;
}, {})
...
상술한 기능은 빠른 실효 전략 아래 일한다.검증되지 않은 청크는 전체 청크의 결과를 무효화합니다.필드 이름과 오류 메시지를 표시하지 못했습니다.글꼴 커밋 기능의 마지막 부분은 다음과 같습니다.
...
if (Object.keys(isValid).length) {
const errField = Object.keys(isValid)[0]
inputWithError.current = errField
const field = getField(errField)
if (!field) {
return
}
field.setCustomValidity(error);
field.reportValidity();
}
여기서 검증기 함수 검사를 통해 오류가 발생하면 사용자 정의 오류를 설정합니다.serialize
사용자 정의 오류를 설정하고 setCustomValidity
사용자에게 이 오류를 표시하는 데 사용이것이 바로 우리가 브라우저 API를 이용하여 React에서 간단하고 빠른 폼을 실현하는 방법이다.
결론
만약 우리가 모든 일을 해 보려고 한다면, 형식은 항상 고통스럽다.그러나 브라우저에는 원하는 양식 유효성 검사를 작성하는 데 도움이 되는 매우 강력한 API가 있습니다.
HTML5를 사용하여 맞춤형 폼을 구축하고 맞춤형 검증을 하는 방법을 알고 있습니다.
표에 대한 관심은 무엇입니까?우리 함께 이 문제를 해결하도록 노력합시다.
댓글로 알려주세요.💬 또는 Twitter에서 및/또는
만약 이 문장이 도움이 된다면 다른 사람과 공유해 주십시오🗣
블로그에 가입하여 받은 편지함에서 새 게시물을 받습니다.
크레디트
애초 2020년 12월 15일https://time2hack.com에 발표됐다.
Reference
이 문제에 관하여(HTML5 및 검증을 사용하여 React에서 빠른 양식 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/time2hack/building-fast-forms-in-react-with-html5-validations-hc2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)