Search code examples
react-nativeformikuse-effectusecallback

React Native - How to submit a Formik from Header


I'm new to Formik and React Native. Currently, my goal is to submit a form via the Navigation header. All the examples I found are to submit the form from the screen page itself. I need help figure out how to properly use the handleSubmit and onSubmit property and setParams to pass the value to the navigation header properly.

Right now, I'm stuck by not sending the values from the form to the useCallBack hook.

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',
  },
});

Solution

  • You're probably using "value" variable with an empty value. You must ref all your form data, inside your < Formik > , let's use street, for an example:

    value={values.street}
    error={touched.street && !isSubmitting && errors.street}
    returnKeyType="next"
    onSubmitEditing={() => refs.number.current?.focus()}
    ref={refs.street}
    

    So declare this ref variable first:

    const refs = {
    street: useRef<any>(null),
    number: useRef<any>(null),
    zipCode: useRef<any>(null),
    //....
    

    If you don't wanna go with REF, so at least declare the variable and try it, like that:

    const [value, setValue] = useState();
    

    Also, the VALUE name is not a good variable name because there are a lot of native things using it. But, considering that you must have taken this from an example, use useState with your form or object and you should be good.

    const [formx, setFormx] = useState();