jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.85k stars 2.78k forks source link

resetForm() displays validation errors [React Native] #3640

Open GunnarAK opened 2 years ago

GunnarAK commented 2 years ago

Bug report

Current Behavior

React Native

The form is not dismissed/unmounted after onSubmit per design. When submitting a form I perform all my business logic (api call), and afterwards I call resetForm() to clear the form for re-use.

The form's input fields are cleared, but each field is validated immediately without any apparent reason.

Expected behavior

Don't re-validate the form on resetForm()

Reproducible example

app/components/TextInput.tsx ``` import React, { useCallback, useRef } from 'react'; import { TextInput as RNTextInput, StyleSheet, TextInputProps, View, } from 'react-native'; import { TouchableOpacity, TouchableWithoutFeedback, } from 'react-native-gesture-handler'; import MaterialIcon from 'react-native-vector-icons/MaterialCommunityIcons'; import defaultStyles from '../config/styles'; export type Props = TextInputProps & { icon?: string; placeholder: string; width?: string | number; }; function TextInput({ icon, width = '100%', ...otherProps }: Props) { const _inputRef = useRef(null); return ( _inputRef.current?.focus()}> {icon && ( )} ); } const styles = StyleSheet.create({ container: { backgroundColor: defaultStyles.colors.light, borderRadius: 25, flexDirection: 'row', marginVertical: 10, padding: 15, }, icon: { alignSelf: 'center', marginRight: 10, }, text: { paddingVertical: 0, // Android textAlignVertical: 'top', }, }); export default TextInput; ```
app/components/forms/FormField.tsx ``` import { useFormikContext } from 'formik'; import React from 'react'; import type { Props as TextInputProps } from '../TextInput'; import TextInput from '../TextInput'; import ErrorMessage from './ErrorMessage'; type Props = TextInputProps & { name: string; }; function FormField({ name, ...otherProps }: Props) { const { setFieldValue, setFieldTouched, errors, touched, values } = useFormikContext(); return ( <> setFieldValue(name, text)} onBlur={() => setFieldTouched(name)} /* @ts-ignore */ value={values[name] || ''} {...otherProps} /> {/* @ts-ignore */} ); } export default FormField; ```
app/components/forms/ErrorMessage.tsx ``` import React from 'react'; import { StyleSheet } from 'react-native'; import Text from '../Text'; type Props = { error: string | void; visible: boolean | void }; function ErrorMessage({ error, visible }: Props) { if (!error || !visible) { return null; } return {error}; } const styles = StyleSheet.create({ error: { color: 'red' }, }); export default ErrorMessage; ```
app/components/forms/SubmitButton.tsx ``` import { useFormikContext } from 'formik'; import React from 'react'; import Button from '../Button'; type Props = { title: string }; function SubmitButton({ title }: Props) { const { handleSubmit } = useFormikContext(); return
Form component to use anywhere ``` import { FormikHelpers } from 'formik'; import * as Yup from 'yup'; import React from 'react'; import { Alert, Keyboard } from 'react-native'; import LocalNotification from '../utility/localNotification'; import { Listing } from '../api/listings'; import messagesApi from '../api/messages'; import { Form, FormField, SubmitButton } from './forms'; interface ContactSellerFormProps { listing: Listing; } interface Values { message: string; } const validationSchema = Yup.object().shape({ message: Yup.string().required().min(2).label('Message'), }); const initialValues: Values = { message: '', }; const ContactSellerForm = ({ listing }: ContactSellerFormProps) => { const sendMessage = async ( values: Values, { resetForm }: FormikHelpers, ) => { try { const result = await messagesApi.send({ ...values, listingId: listing.id, }); if (!result.ok) { console.error('Error', result); return Alert.alert( 'Error', 'Could not send your message to the seller.', ); } resetForm(); // Dismiss keyboard Keyboard.dismiss(); } catch (error) { console.warn('sendMessage failed', error); } }; return (
); }; export default ContactSellerForm; ```

Suggested solution(s)

Disable/block re-validation on resetForm()

Additional context

x

Your environment

Software Version(s)
Formik 2.2.9
React 17.0.2
TypeScript 4.8.2
React Native 0.68.3
npm 8.1.0
Operating System MacOS Monterey 12.5.1
GunnarAK commented 2 years ago

setting values/errors/touched before or after resetForm() was not successful either.

Example:

resetForm();
setValues({});
setErrors({});
setTouched({});

// == OR ==
resetForm({ values: initialValues, errors: {}, touched: { message: false} });

😕

pniby commented 1 year ago

I'm also facing same issue. Did you fix it?

GunnarAK commented 1 year ago

No I haven't sorry. The project in which I was facing this was just part of a course, so it didn't bother me that much. Looking at the code I resorted to just calling resetForm(). But I don't recall actually having fixed the issue.

pniby commented 1 year ago

No I haven't sorry. The project in which I was facing this was just part of a course, so it didn't bother me that much. Looking at the code I resorted to just calling resetForm(). But I don't recall actually having fixed the issue.

Ok. Thanks for the reply.

Nziranziza commented 1 year ago

I am also having similar issue working with react native 0.70.5

thiagosimoes1305 commented 1 year ago

Solution: const formikRef = useRef(null)

const onSubmit = useCallback((values, { setSubmitting, resetForm }) => {
    resetForm()
    formikRef.current.setErrors({}) <------ this code
    setSubmitting(false)
  }, [])

<Formik ref={formikRef} initialValues={initialValues} ...

cloudever commented 1 year ago

Following code in a submit handler:

helpers.resetForm();
helpers.setErrors({});
helpers.setErrors({}); // <--- Second call

solves this issue for me 😆

pniby commented 1 year ago

@cloudever can you share the full code?

lesthoward commented 3 weeks ago
       onSubmit={(values, {resetForm, setSubmitting}) => {
            Keyboard.dismiss();
            setTimeout(() => {
              setSubmitting(false);
              resetForm();
            }, 100);
          }}