前文中我特別提起Redux Form以及redux-form的問題,我覺得學(xué)習(xí)Formik你不得不提它們,當(dāng)然還有它們的「老祖宗」React;既然選擇了,那么你必須按照這個方向走下去。有一句叫作“沒有最好,只有更好”。這句話應(yīng)用于開源技術(shù)的學(xué)習(xí)上也很貼切,基于React技術(shù)的表單開發(fā),到底哪一種方案最好,相信國內(nèi)外很多高手都在探討這個問題。較早的redux-form這個自不必說了,如果你選擇而且熱戀著redux,那么很不難不注意redux-form,但是redux-form盡管在一定程度上簡化了基于redux的表單的開發(fā)但是也的確讓人覺得很辛苦,這一點從我自己追蹤學(xué)習(xí)redux-form來說就有深刻體會。而現(xiàn)在的Formik開發(fā)者也正是體會到了這種痛苦,于是基于前輩們的作者又開發(fā)了Formik,并提供口號“Build forms in React, without the tears”——能不能浪漫一些翻譯為“React表單不相信眼淚”?(受名片《莫斯科不相信眼淚》誘發(fā))??傊痪湓挘挥邢攘私饬碎_發(fā)復(fù)雜React表單的痛苦你才會深刻體會學(xué)習(xí)的Formik的必要性。當(dāng)然,F(xiàn)ormik本身也很年輕,只有1歲,版本目前是1.0.2。但是,我相信這個庫會很快地發(fā)展下去,除非短時間內(nèi)出現(xiàn)了比Formik更為優(yōu)秀的React Form解決方案。
隨州網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)建站從2013年成立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。
下面的代碼片斷給出了
import React from 'react';
import { Formik, Field } from 'formik';
const Example = () => (
My Form
{
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={(props: FormikProps) => (
)}
/>
);
const CustomInputComponent: React.SFC<
FieldProps & CustomInputProps
> = ({
field, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => (
{touched[field.name] &&
errors[field.name] && {errors[field.name]}}
);
思路是使用:validate?: (value: any) => undefined | string | Promise
你可以通過把一個具有校驗功能的函數(shù)傳遞給validate屬性來執(zhí)行獨立的字段層面的校驗。此功能的觸發(fā)將會相應(yīng)于在
// Synchronous validation for Field
const validate = value => {
let errorMessage;
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
errorMessage = 'Invalid email address';
}
return errorMessage;
};
請參考下面的代碼:
// Async validation for Field
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = value => {
return sleep(2000).then(() => {
if (['admin', 'null', 'god'].includes(value)) {
throw 'Nice try';
}
});
};
【注意】考慮到i18n庫(實現(xiàn)界面的多語言版本所需要)的使用方面,TypeScript檢驗類型比較寬松——允許你返回一個函數(shù)(例如i18n('invalid'))。
當(dāng)你沒有使用定制組件而且你想訪問由
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik';
//下面提供的表單示例中有一個可編輯的列表。緊鄰每一個輸入字段是控制插入與刪除的按鈕。
//若列表為空,那么會顯示一個添加項目的按鈕。
export const FriendList = () => (
Friend List
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
)}
/>
);
這個屬性指向values中相關(guān)聯(lián)的鍵的名字或者路徑。
默認(rèn)值為true,用來決定在運行任何數(shù)組操作后執(zhí)行還是不執(zhí)行表單校驗。
你還可以遍歷一個對象數(shù)組——通過使用一種格式為object[index]property或者是object.index.property的方法,它們分別作為
當(dāng)使用
第一,如果你使用validationSchema,并且你的表單正好有數(shù)組校驗需求 (例如一個最小長度值),以及在嵌套數(shù)組字段需求情況下,顯示錯誤信息時也需要小心一些——Formik/Yup會在外部顯示校驗錯誤信息。例如:
const schema = Yup.object().shape({
friends: Yup.array()
.of(
Yup.object().shape({
name: Yup.string()
.min(4, 'too short')
.required('Required'), // these constraints take precedence
salary: Yup.string()
.min(3, 'cmon')
.required('Required'), // these constraints take precedence
})
)
.required('Must have friends') // these constraints are shown if and only if inner constraints are satisfied
.min(3, 'Minimum of 3 friends'),
});
既然Yup和你的定制校驗函數(shù)總會輸出字符串形式的錯誤信息,那么你需要設(shè)法確定在顯示時是否你的嵌套錯誤信息是一個數(shù)組或者是一個字符串。
于是,為了顯示“Must have friends”和“Minimum of 3 friends”(這是我們示例中的數(shù)組校驗約束)......
// within a `FieldArray`'s render
const FriendArrayErrors = errors =>
errors.friends ? {errors.friends} : null; // app will crash
// within a FieldArray
's render
const FriendArrayErrors = errors =>
typeof errors.friends === 'string' ? {errors.friends} : null;
對于嵌套的字段錯誤信息而言,你應(yīng)當(dāng)假定并沒有預(yù)告定義對象的哪一部分——除非你事先檢查了它。這樣一來,你可以創(chuàng)建一個定制的
import { Field, getIn } from 'formik';
const ErrorMessage = ({ name }) => (
{
const error = getIn(form.errors, name);
const touch = getIn(form.touched, name);
return touch && error ? error : null;
}}
/>
);
//使用上面定制組件的情形:
; // => null, 'too short', or 'required'
【注意】在Formik v0.12 / 1.0中,支持把一個新的meta屬性添加給Field和FieldArray,此屬性用于為你提供類似于error和touch這樣的相關(guān)的元數(shù)據(jù)信息,這可以使你免于不得不使用Formik或者lodash的getIn方法來檢查是否你自己定義了路徑部分。
下面的幫助函數(shù)可以經(jīng)由render這個屬性用來輔助操作字段數(shù)組:
有三種方式可以渲染
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => (
Friend List
(
(
)}
/>
/>
);
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik'
export const FriendList = () => (
Friend List
(
/>
);
// 除去數(shù)組幫助函數(shù),F(xiàn)ormik中的狀態(tài)和它本身的幫助函數(shù)
// (例如values, touched, setXXX, etc)都是經(jīng)由表單的prop形式提供的
//
export const MyDynamicForm = ({
move, swap, push, insert, unshift, pop, form
}) => (
);
類似于
import React from 'react';
import { Formik, Field, Form } from 'formik';
const Example = () => (
My Form
{
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
component={MyForm}
/>
);
const MyForm = () => (
);
此方法用于構(gòu)建一個高階React組件類,該類把props和form handlers (即"FormikBag")傳遞進你的根據(jù)提供的選項生成的組件中。
在你的內(nèi)部表單組件是一個無狀態(tài)函數(shù)組件情況下,你可以使用displayName這個選項來給組件一個合適的名字——從而從React DevTools(調(diào)試工具,在我以前的博客中專門討論過)中可以更容易地觀察到它。如果指定了這個屬性,你的包裝表單中將顯示成這樣——Formik(displayName)。如果忽略這個屬性,顯示樣式為Formik(Component)。不過,這個選項對于類組件(例如class XXXXX extends React.Component {..})并不必要。
默認(rèn)為false。這個屬性用來控制當(dāng)包裝組件屬性變化(使用深度相等比較,using deep equality)時Formik是否應(yīng)當(dāng)復(fù)位表單。
這是表單提交處理器。其中的參照是描述你的表單的values對象,還有“FormikBag”。其中,F(xiàn)ormikBag:(a)包含一個對象,此對象擁有被注入的屬性和方法的子集(如所有類似于set
默認(rèn)為 false. 此選擇用于控制表單加載前isValid屬性的初始值。你還可以傳遞一個函數(shù)。 Useful for situations when you want to enable/disable a submit and reset buttons on initial mount.
If this option is specified, then Formik will transfer its results into updatable form state and make these values available to the new component as props.values. If mapPropsToValues is not specified, then Formik will map all props that are not functions to the inner component's props.values. That is, if you omit it, Formik will only pass props where typeof props[k] !== 'function', where k is some key.
Even if your form is not receiving any props from its parent, use mapPropsToValues to initialize your forms empty state.
【注意】Formik作者極力推薦使用validationSchema與Yup進行表單校驗。但是,就校驗這個任務(wù)而言,你可以任意選擇自己喜歡的直觀高效的校驗方案。
使用函數(shù)校驗表單的values對象。這個函數(shù)可能是下面兩種情形之一:
(A)同步函數(shù),并且返回一個對象errors。
// Synchronous validation
const validate = (values, props) => {
let errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address';
}
//...
return errors;
};
(B)異步函數(shù),它返回一個包含errors對象的Promise。
// Async Validation
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const validate = (values, props) => {
return sleep(2000).then(() => {
let errors = {};
if (['admin', 'null', 'god'].includes(values.username)) {
errors.username = 'Nice try';
}
// ...
if (Object.keys(errors).length) {
throw errors;
}
});
};
默認(rèn)為true。在blur事件觸發(fā)時(更具體一些說,是當(dāng)調(diào)用handleBlur,setFieldTouched或者是setTouched時)使用這個選項進行校驗。
默認(rèn)為true。 通過此選項告訴Formik在change事件或者相關(guān)方法(更具體一些說,是當(dāng)調(diào)用handleChange,setFieldValue或者setValues)觸發(fā)時進行校驗。
這個屬性極為重要,它定義了一個Yup模式( schema)或者是返回Yup模式的一個函數(shù)。在校驗是使用這個屬性是非常有用的。錯誤信息被映射到內(nèi)部組件的errors對象。它的鍵應(yīng)當(dāng)匹配values中對應(yīng)的鍵。
這些與
connect()是一個高階組件,它用于把原始的Formik上下文以屬性方式(命名為formik)注入到內(nèi)部組件中。另外一個有趣的事實是:Formik在底層上也利用了connect()來封裝
import { connect } from 'formik';
const SubmitCount = ({ formik }) => {formik.submitCount};
export default connect(SubmitCount);