使用Formik轻松开发更高质量的React表单(二)使用指南
一个基本的例子设想你要开发一个可以编辑用户数据的表单。不过,你的用户API端使用了具有类似下面的嵌套对象表达: {
id: string,email: string,social: {
facebook: string,twitter: string,// ...
}
}
最后,我们想使开发的对话框表单能够接收下面几个属性(props):user,updateUser和onClose(显然,user是一个对象,updateUser和onClose却都是两个方法)。 // User.js
import React from 'react';
import Dialog from 'MySuperDialog';
import { Formik } from 'formik';
const EditUserDialog = ({ user,updateUser,onClose }) => {
return (
<Dialog onClose={onClose}>
<h1>Edit User</h1>
<Formik
initialValues={user /** { email,social } */}
onSubmit={(values,actions) => {
CallMyApi(user.id,values).then(
updatedUser => {
actions.setSubmitting(false);
updateUser(updatedUser),onClose();
},error => {
actions.setSubmitting(false);
actions.setErrors(transformMyAPIErrorToAnObject(error));
}
);
}}
render={({
values,errors,touched,handleBlur,handleChange,handleSubmit,isSubmitting,}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && <div>{errors.email}</div>}
<input
type="text"
name="social.facebook"
onChange={handleChange}
onBlur={handleBlur}
value={values.social.facebook}
/>
{errors.social &&
errors.social.facebook &&
touched.facebook && <div>{errors.social.facebook}</div>}
<input
type="text"
name="social.twitter"
onChange={handleChange}
onBlur={handleBlur}
value={values.social.twitter}
/>
{errors.social &&
errors.social.twitter &&
touched.twitter && <div>{errors.social.twitter}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</form>
)}
/>
</Dialog>
);
};
简化编码为了简化表单组件的编码,Formik还提供了两个帮助API:
于是,下面的代码与前面一致,只是使用<Form />和<Field />这两个API进行了改写: // EditUserDialog.js
import React from 'react';
import Dialog from 'MySuperDialog';
import { Formik,Field,Form } from 'formik';
const EditUserDialog = ({ user,error => {
actions.setSubmitting(false);
actions.setErrors(transformMyAPIErrorToAnObject(error));
}
);
}}
render={({ errors,isSubmitting }) => (
<Form>
<Field type="email" name="email" />
{errors.email && touched.social.email && <div>{errors.email}</div>}
<Field type="text" name="social.facebook" />
{errors.social.facebook &&
touched.social.facebook && <div>{errors.social.facebook}</div>}
<Field type="text" name="social.twitter" />
{errors.social.twitter &&
touched.social.twitter && <div>{errors.social.twitter}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
/>
</Dialog>
);
};
React Native开发问题Formik与React Native 和React Native Web开发完全兼容。然而,由于ReactDOM和React Native表单处理与文本输入方式的不同,有两个区别值得注意。本文将介绍这个问题并推荐更佳使用方式。 在进一步讨论前,先来最简要地概括一下如何在React Native中使用Formik。下面的轮廓代码展示了两者的关键区别: // Formik +React Native示例
import React from 'react';
import { Button,TextInput,View } from 'react-native';
import { withFormik } from 'formik';
const enhancer = withFormik({
/*...*/
});
const MyReactNativeForm = props => (
<View>
<TextInput
onChangeText={props.handleChange('email')}
onBlur={props.handleBlur('email')}
value={props.values.email}
/>
<Button onPress={props.handleSubmit} title="Submit" />
</View>
);
export default enhancer(MyReactNativeForm);
从上面代码中,你会明显注意到在React Native 和React DOM开发中使用Formik存在如下不同: (1)Formik的props.handleSubmit被传递给一个<Button onPress={...} />,而不是HTML <form onSubmit={...} /> 组件(因为在React Native中没有<form />元素)。 (2)<TextInput />使用Formik的props.handleChange(fieldName)和handleBlur(fieldName),而不是直接把回调函数赋值给props,因为我们必须从某处得到fieldName,而在ReactNative中我们无法你在Web中一样自动获取它(使用input的name属性)。作为可选方案,你还可以使用 setFieldValue(fieldName,value) 和setTouched(fieldName,bool) 这两个函数。 避免在render中创建新函数如果因某种原因你想在每一个render中避免创建新函数,那么我建议你把React Native的 <TextInput /> 当作它是一个第三方提供的定制输入元素:
请参考下面的代码: // FormikReactNativeTextInput.js
import * as React from 'react';
import { TextInput } from 'react-native';
export default class FormikReactNativeTextInput extends React.Component {
handleChange = (value: string) => {
// remember that onChangeText will be Formik's setFieldValue
this.props.onChangeText(this.props.name,value);
};
render() {
// we want to pass through all the props except for onChangeText
const { onChangeText,...otherProps } = this.props;
return (
<TextInput
onChangeText={this.handleChange}
{...otherProps} // IRL,you should be more explicit when using TS
/>
);
}
}
然后,你可以像下面这样使用这个定制输入组件: // MyReactNativeForm.js
import { View,Button } from 'react-native';
import TextInput from './FormikReactNativeTextInput';
import { Formik } from 'formik';
const MyReactNativeForm = props => (
<View>
<Formik
onSubmit={(values,actions) => {
setTimeout(() => {
console.log(JSON.stringify(values,null,2));
actions.setSubmitting(false);
},1000);
}}
render={props => (
<View>
<TextInput
name="email"
onChangeText={props.setFieldValue}
value={props.values.email}
/>
<Button title="submit" onPress={props.handleSubmit} />
</View>
)}
/>
</View>
);
export default MyReactNativeForm;
使用TypeScript开发Formik表单(一)TypeScript类型Formik是使用TypeScript写的,Formik中的类型十分类似于React Router 4中的<Route>。 Render props (<Formik /> and <Field />)
import * as React from 'react';
import { Formik,FormikProps,Form,FieldProps } from 'formik';
interface MyFormValues {
firstName: string;
}
export const MyApp: React.SFC<{} /* whatever */> = () => {
return (
<div>
<h1>My Example</h1>
<Formik
initialValues={{ firstName: '' }}
onSubmit={(values: MyFormValues) => alert(JSON.stringify(values))}
render={(formikBag: FormikProps<MyFormValues>) => (
<Form>
<Field
name="firstName"
render={({ field,form }: FieldProps<MyFormValues>) => (
<div>
<input type="text" {...field} placeholder="First Name" />
{form.touched.firstName &&
form.errors.firstName &&
form.errors.firstName}
</div>
)}
/>
</Form>
)}
/>
</div>
);
};
(二)使用withFormik()import React from 'react';
import * as Yup from 'yup';
import { withFormik,FormikErrors,Field } from 'formik';
// Shape of form values
interface FormValues {
email: string;
password: string;
}
interface OtherProps {
message: string;
}
顺便提醒一下,你可以使用InjectedFormikProps<OtherProps,FormValues>来代替下面的实现方式。本质上,它们是相同的,只不过InjectedFormikProps是当Formik仅输出一个HOC(高阶组件)时的代替而已。而且,这个方法灵活性差一些,因为它需要对所有属性(props)进行包装。 const InnerForm = (props: OtherProps & FormikProps<FormValues>) => {
const { touched,message } = props;
return (
<Form>
<h1>{message}</h1>
<Field type="email" name="email" />
{touched.email && errors.email && <div>{errors.email}</div>}
<Field type="password" name="password" />
{touched.password && errors.password && <div>{errors.password}</div>}
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
);
};
//MyForm接收的props的类型
interface MyFormProps {
initialEmail?: string;
message: string; // if this passed all the way through you might do this or make a union type
}
//使用withFormik高阶组件包装你的表单
const MyForm = withFormik<MyFormProps,FormValues>({
// Transform outer props into form values
mapPropsToValues: props => {
return {
email: props.initialEmail || '',password: '',};
},//添加定制的校验函数(也有可能是异步的)
validate: (values: FormValues) => {
let errors: FormikErrors = {};
if (!values.email) {
errors.email = 'Required';
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid email address';
}
return errors;
},handleSubmit: values => {
// do submitting things
},})(InnerForm);
// 你可以在任何地方使用<MyForm />
const Basic = () => (
<div>
<h1>My App</h1>
<p>This can be anywhere in your application</p>
<MyForm message="Sign up" />
</div>
);
export default Basic;
Formik表单提交原理要在Formik中提交表单,你需要以某种方式触发 handleSubmit(e) 或者submitForm属性调用(在Formik中这两个方法都是以属性的方式提供的)。 当调用其中一个方法时,Formik每次都会执行下面的伪代码: (一)预提交 FAQ(1)Q:怎么判定提交处理器(submission handler)正在执行中? (2)Q:为什么在提交前Formik要“润色一下(touch)”表单中所有字段? (3)Q:如何避免两次重复提交? (4)Q:如何得知表单在提交前正在校验中?A:如果isValidating为true而且isSubmitting也为true的话,...... (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 在GtkBuilder XML中的GtkDialog中使用预定义的响应ID?
- swift学习笔记(1)--基本数据类型
- XML 定义一些需要的控件背景样式
- 正则表达式
- c – 从OpenCV中旋转的图像旋转后点
- ajax – 如何在Google Vizualization Bar Charts中自定义工
- c# – 在无限循环中创建的对象的垃圾收集
- React Native-14.React Native 常用API及实践 AsyncStorag
- ruby-on-rails – Ruby on Rails应用程序上的AssociationTy
- c# – 如何使用Format(“blah {0}”,someValue)签名来实现M
