React Native - 如何从 Header 提交 Formik
React Native - How to submit a Formik from Header
我是 Formik 和 React Native 的新手。目前,我的目标是通过导航 header 提交表单。我找到的所有示例都是从屏幕页面本身提交表单。我需要帮助弄清楚如何正确使用 handleSubmit 和 onSubmit 属性 和 setParams将值正确传递给导航header。
现在,我无法将表单中的值发送到 useCallBack 挂钩。
import React, {useState, useEffect, useCallback} from 'react';
import {StyleSheet, View, Button, Text} from 'react-native';
import {Input} from 'react-native-elements';
import {useDispatch} from 'react-redux';
import Colors from '../../constants/Colors';
import {
HeaderButtons,
HeaderButton,
Item,
} from 'react-navigation-header-buttons';
import {Ionicons} from '@expo/vector-icons';
import {CommonActions} from '@react-navigation/native';
import * as prodActions from '../../store/actions/products';
import {Formik} from 'formik';
import * as Yup from 'yup';
const AddProduct = ({navigation}) => {
const dispatch = useDispatch();
const submitHandler = useCallback(() => {
dispatch(prodActions.addProduct(value));
}, [value]);
useEffect(() => {
navigation.dispatch(CommonActions.setParams({submitForm: submitHandler}));
}, [submitHandler]);
return (
<View style={styles.screen}>
<Formik
initialValues={{title: '', description: '', imageUrl: ''}}
validationSchema={Yup.object({
title: Yup.string().required('please input your title'),
description: Yup.string().required('please input your description'),
imageUrl: Yup.string()
//.email('Please input a valid email.')
.required('Please input an email address.'),
})}
onSubmit={submitHandler}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
touched,
errors,
}) => (
<View>
<Input
label="Title"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('title')}
onBlur={handleBlur('title')}
value={values.title}
// errorMessage={errors.title}
/>
{touched.title && errors.title ? (
<Text style={styles.error}>{errors.title}</Text>
) : null}
<Input
label="Description"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('description')}
onBlur={handleBlur('description')}
value={values.description}
/>
{touched.description && errors.description ? (
<Text style={styles.error}>{errors.description}</Text>
) : null}
<Input
label="Image URL"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('imageUrl')}
onBlur={handleBlur('imageUrl')}
value={values.imageUrl}
/>
{touched.imageUrl && errors.imageUrl ? (
<Text style={styles.error}>{errors.imageUrl}</Text>
) : null}
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
</View>
);
};
const IoniconsHeaderButton = (props) => (
<HeaderButton
IconComponent={Ionicons}
iconSize={23}
color="white"
{...props}
/>
);
export const AddProductHeaderOptions = (navData) => {
const updateForm = navData.route.params.submitForm;
return {
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={updateForm} />
</HeaderButtons>
);
},
};
};
export default AddProduct;
const styles = StyleSheet.create({
screen: {
flex: 1,
marginHorizontal: 20,
marginTop: 20,
},
error: {
marginLeft: 8,
fontSize: 14,
color: 'red',
},
});
您可能正在使用具有空值的“值”变量。您必须在 中引用所有表单数据,例如使用街道:
value={values.street}
error={touched.street && !isSubmitting && errors.street}
returnKeyType="next"
onSubmitEditing={() => refs.number.current?.focus()}
ref={refs.street}
所以先声明这个ref变量:
const refs = {
street: useRef<any>(null),
number: useRef<any>(null),
zipCode: useRef<any>(null),
//....
如果您不想使用 REF,那么至少声明变量并尝试一下,就像这样:
const [value, setValue] = useState();
另外,VALUE 名称不是一个好的变量名,因为有很多本地的东西在使用它。但是,考虑到您一定是从示例中获取的,将 useState 与您的表单或对象一起使用,您应该会很好。
const [formx, setFormx] = useState();
对于那些使用 React-Navigation 5 和 Formik 1 的人。5.x+ 这是一个很好的解决方案。
声明函数组件的 ref 变量 outside/inside 以稍后附加到您的
let formRef;//or you can use const formRef = useRef(); inside function component
如果您在函数组件外声明 formRef,请使用 React hook {useRef} 更新 ref
formRef = useRef();
渲染
<Formik innerRef={formRef} />
最后在你的 header 按钮中调用这个
navigation.setOptions({
headerRight: () => (
<Button onPress={() => {
if (formRef.current) {
formRef.current.handleSubmit()
}
}}/>
)
});
另一个我认为可能更好的解决方案是使用 useFormik,它使您可以访问所有表单处理程序和表单元数据:
const {
handleSubmit,
handleChange,
isValid,
} = useFormik({
initialValues: {...},
validationSchema: yupValidationSchema,
onSubmit: (formValues) => {
//save here...
}
});
然后您可以使用官方文档中建议的 useLayoutEffect 简单地将 handleSubmit 引用传递给您的右侧 header 导航:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={handleSubmit}/>
</HeaderButtons>
);
}
});
});
我认为这种方法很明确,因为您不需要处理使用 renderProps 方法的所有结构缺点
<Formkik>
{({ handleSubmit }) => (
jsx here...
)}
</Formik>
我是 Formik 和 React Native 的新手。目前,我的目标是通过导航 header 提交表单。我找到的所有示例都是从屏幕页面本身提交表单。我需要帮助弄清楚如何正确使用 handleSubmit 和 onSubmit 属性 和 setParams将值正确传递给导航header。
现在,我无法将表单中的值发送到 useCallBack 挂钩。
import React, {useState, useEffect, useCallback} from 'react';
import {StyleSheet, View, Button, Text} from 'react-native';
import {Input} from 'react-native-elements';
import {useDispatch} from 'react-redux';
import Colors from '../../constants/Colors';
import {
HeaderButtons,
HeaderButton,
Item,
} from 'react-navigation-header-buttons';
import {Ionicons} from '@expo/vector-icons';
import {CommonActions} from '@react-navigation/native';
import * as prodActions from '../../store/actions/products';
import {Formik} from 'formik';
import * as Yup from 'yup';
const AddProduct = ({navigation}) => {
const dispatch = useDispatch();
const submitHandler = useCallback(() => {
dispatch(prodActions.addProduct(value));
}, [value]);
useEffect(() => {
navigation.dispatch(CommonActions.setParams({submitForm: submitHandler}));
}, [submitHandler]);
return (
<View style={styles.screen}>
<Formik
initialValues={{title: '', description: '', imageUrl: ''}}
validationSchema={Yup.object({
title: Yup.string().required('please input your title'),
description: Yup.string().required('please input your description'),
imageUrl: Yup.string()
//.email('Please input a valid email.')
.required('Please input an email address.'),
})}
onSubmit={submitHandler}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
touched,
errors,
}) => (
<View>
<Input
label="Title"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('title')}
onBlur={handleBlur('title')}
value={values.title}
// errorMessage={errors.title}
/>
{touched.title && errors.title ? (
<Text style={styles.error}>{errors.title}</Text>
) : null}
<Input
label="Description"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('description')}
onBlur={handleBlur('description')}
value={values.description}
/>
{touched.description && errors.description ? (
<Text style={styles.error}>{errors.description}</Text>
) : null}
<Input
label="Image URL"
labelStyle={{color: Colors.accent}}
onChangeText={handleChange('imageUrl')}
onBlur={handleBlur('imageUrl')}
value={values.imageUrl}
/>
{touched.imageUrl && errors.imageUrl ? (
<Text style={styles.error}>{errors.imageUrl}</Text>
) : null}
<Button onPress={handleSubmit} title="Submit" />
</View>
)}
</Formik>
</View>
);
};
const IoniconsHeaderButton = (props) => (
<HeaderButton
IconComponent={Ionicons}
iconSize={23}
color="white"
{...props}
/>
);
export const AddProductHeaderOptions = (navData) => {
const updateForm = navData.route.params.submitForm;
return {
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={updateForm} />
</HeaderButtons>
);
},
};
};
export default AddProduct;
const styles = StyleSheet.create({
screen: {
flex: 1,
marginHorizontal: 20,
marginTop: 20,
},
error: {
marginLeft: 8,
fontSize: 14,
color: 'red',
},
});
您可能正在使用具有空值的“值”变量。您必须在
value={values.street}
error={touched.street && !isSubmitting && errors.street}
returnKeyType="next"
onSubmitEditing={() => refs.number.current?.focus()}
ref={refs.street}
所以先声明这个ref变量:
const refs = {
street: useRef<any>(null),
number: useRef<any>(null),
zipCode: useRef<any>(null),
//....
如果您不想使用 REF,那么至少声明变量并尝试一下,就像这样:
const [value, setValue] = useState();
另外,VALUE 名称不是一个好的变量名,因为有很多本地的东西在使用它。但是,考虑到您一定是从示例中获取的,将 useState 与您的表单或对象一起使用,您应该会很好。
const [formx, setFormx] = useState();
对于那些使用 React-Navigation 5 和 Formik 1 的人。5.x+ 这是一个很好的解决方案。 声明函数组件的 ref 变量 outside/inside 以稍后附加到您的
let formRef;//or you can use const formRef = useRef(); inside function component
如果您在函数组件外声明 formRef,请使用 React hook {useRef} 更新 ref
formRef = useRef();
渲染
<Formik innerRef={formRef} />
最后在你的 header 按钮中调用这个
navigation.setOptions({
headerRight: () => (
<Button onPress={() => {
if (formRef.current) {
formRef.current.handleSubmit()
}
}}/>
)
});
另一个我认为可能更好的解决方案是使用 useFormik,它使您可以访问所有表单处理程序和表单元数据:
const {
handleSubmit,
handleChange,
isValid,
} = useFormik({
initialValues: {...},
validationSchema: yupValidationSchema,
onSubmit: (formValues) => {
//save here...
}
});
然后您可以使用官方文档中建议的 useLayoutEffect 简单地将 handleSubmit 引用传递给您的右侧 header 导航:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={handleSubmit}/>
</HeaderButtons>
);
}
});
});
我认为这种方法很明确,因为您不需要处理使用 renderProps 方法的所有结构缺点
<Formkik>
{({ handleSubmit }) => (
jsx here...
)}
</Formik>