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',
},
});
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();
For those who use React-Navigation 5 and Formik 1.5.x+ Here is a good solution.
Declare a ref variable outside/inside of your function component to later Attach to your
let formRef;//or you can use const formRef = useRef(); inside function component
If you declare formRef outside your function component use React hook {useRef} to update the ref
formRef = useRef();
Render
<Formik innerRef={formRef} />
And finally in your header button call this
navigation.setOptions({
headerRight: () => (
<Button onPress={() => {
if (formRef.current) {
formRef.current.handleSubmit()
}
}}/>
)
});
Another solution, which I think could be better is to use the useFormik, which gives you access to all the form handlers and form meta:
const {
handleSubmit,
handleChange,
isValid,
} = useFormik({
initialValues: {...},
validationSchema: yupValidationSchema,
onSubmit: (formValues) => {
//save here...
}
});
Then you can simply pass the handleSubmit reference to your right header navigation using the useLayoutEffect as proposed in the official documentation:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<HeaderButtons HeaderButtonComponent={IoniconsHeaderButton}>
<Item title="add" iconName="save-outline" onPress={handleSubmit}/>
</HeaderButtons>
);
}
});
});
This approach is clear in my opinion, since you don't need to handle all the structural disadvantages of using the renderProps approach with
<Formkik>
{({ handleSubmit }) => (
jsx here...
)}
</Formik>
Related
I'm using Formik and was wondering how I go about clearing the errors and form values when leaving a screen.
For example, a user tries to submit the form with no values and the errors are displayed:
When the user then navigates to a different screen and then comes back those errors are still present. Is there a way to clear these? Can I access Formik methods within a useEffect hook as an example?
This is my implementation so far:
export const SignIn = ({route, navigation}) => {
const formValidationSchema = Yup.object().shape({
signInEmail: Yup.string()
.required('Email address is required')
.email('Please provide a valid email address')
.label('Email'),
signInPassword: Yup.string()
.required('Password is required')
.label('Password'),
});
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
return (
<Formik
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values, formikActions) => {
handleFormSubmit(values);
}}>
{({handleChange, handleSubmit, errors}) => (
<>
<SignInForm
messages={errors}
navigation={navigation}
handleFormSubmit={handleSubmit}
/>
</>
)}
</Formik>
)
}
The problem here is that a screen does not get unmounted if we navigate to a different screen and the initial values of a Formik form will only be set on screen mount.
I have created a minimal example with one field and I navigate whenever the submit button is executed to a different Screen in a Stack.Navigator.
Notice that the onSubmit function is usually not fired if there are errors in your form fields. However, since I wanted to provide a quick test, I navigate by hand by calling the function directly.
If we navigate back by pressing the onBack button of the navigator, the form fields will be reseted to the default values and all errors will be reseted automatically.
We can trigger this by hand using the Formik innerRef prop and a focus listener.
For testing this, you should do the following.
Type something, and remove it. Notice the error message that is rendered below the input field.
Navigate to the screen using the submit button.
Go back.
Expected result: no error message.
Type something. Expected result: no error message.
Navigate on submit.
Go back.
Expected result: no error message, no content in field.
In principal, this will work with every navigator, e.g. changing a Tab in a Tab.Navigator and it will reset both, the errors and the field's content.
The key part is given by the following code snippet.
const ref = useRef(null)
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if (ref?.current) {
ref.current.values = initialFormValues
ref.current.setErrors({})
console.log(ref.current)
}
});
return unsubscribe;
}, [navigation, initialFormValues]);
return (
<Formik
innerRef={ref}
isInitialValid={true}
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values) => {
console.log("whatever")
}}>
...
The full code is given as follows.
import React, { useRef} from 'react';
import { StyleSheet, Text, View,TextInput, Button } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { Formik } from 'formik';
import * as Yup from 'yup';
export const SignIn = ({route, navigation}) => {
const formValidationSchema = Yup.object().shape({
signInEmail: Yup.string()
.required('Email address is required')
.label('Email'),
});
const ref = useRef(null)
const initialFormValues = {
signInEmail: '',
signInPassword: '',
};
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if (ref?.current) {
ref.current.values = initialFormValues
ref.current.setErrors({})
console.log(ref.current)
}
});
return unsubscribe;
}, [navigation, initialFormValues]);
function handleFormSubmit(values) {
navigation.navigate("SomeScreen")
}
return (
<Formik
innerRef={ref}
isInitialValid={true}
initialValues={initialFormValues}
validationSchema={formValidationSchema}
onSubmit={(values) => {
console.log("whatever")
}}>
{({handleChange, handleSubmit, errors, values}) => (
<>
<View>
<TextInput
style={{height: 30}}
placeholder={"Placeholder mail"}
onChangeText={handleChange('signInEmail')}
value={values.signInEmail}
/>
{errors.signInEmail ?
<Text style={{ fontSize: 10, color: 'red' }}>{errors.signInEmail}</Text> : null
}
<Button onPress={() => {
handleSubmit()
handleFormSubmit()}} title="Submit" />
</View>
</>
)}
</Formik>
)
}
export function SomeOtherScreen(props) {
return <></>
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Form" component={SignIn} />
<Stack.Screen name="SomeScreen" component={SomeOtherScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
This should be possible with the useFocusEffect hook from react navigation. It gets triggered on first time and every time the screen comes in focus.
useFocusEffect(
React.useCallback(() => {
// set Formik Errors to empty object
setErrors({});
// Since your clearing errors, you should also reset the formik values
setSignInEmail('');
setSignInPassword('');
return () => {}; // gets called on unfocus. I don't think you need this
}, [])
);
I am following a react native course. I am trying to create an iconName = "ios-menu" but when trying to open it it does not work and it tells me that "TypeError: navData.navigation.toggleDrawer is not a function" I can't find a solution I would like to know why this happens somebody could help me.
enter code here
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { CATEGORIES } from '../data/dummy-data';
import HeaderButton from'../components/HeaderButton';
import CategoryGridTile from '../components/CategoryGridTile';
import { DrawerActions } from 'react-navigation-drawer';
import { createDrawerNavigator } from 'react-navigation-drawer';
const CategoriesScreen = props => {
const renderGridItem = itemData => {
return (
<CategoryGridTile
title={itemData.item.title}
color={itemData.item.color}
onSelect={() => {
props.navigation.navigate({
routeName: 'CategoryMeals',
params: {
categoryId: itemData.item.id
}
});
}}
/>
);
};
return (
<FlatList
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderGridItem}
numColumns={2}
/>
);
};
CategoriesScreen.navigationOptions = navData => {
return {
headerTitle: 'Meal Categories',
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName="ios-menu"
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
)
};
};
In your DrawerNavigator.js when you create it, Probably you didn't export the module correctly at export default createAppContainer(DrawerNavigator "not stack but drawer");
Probably the reason why the toggleDrawer() function wasn't recognised as the react-navigation-drawer methods wasn't passed to App.js
I'm very new to React Native. I've got the following page which loads dates and results from a database, all displaying ok:
import React, { useEffect, useState } from "react";
import {
Text,
FlatList,
StyleSheet,
TouchableOpacity,
Platform,
} from "react-native";
import AsyncStorage from "#react-native-community/async-storage";
import Colors from "../../../res/Colors/Colors";
import { s, scale } from "react-native-size-matters";
import Axios from "axios";
import moment from "moment";
import { SafeAreaView} from "react-navigation";
export default function HistoryScreen({ navigation }) {
const [userDetails, setUserDetails] = useState(null);
const [loading, setLoading] = useState(true);
const [Star, setStar] = useState(null);
const [people, setPeople] = useState(null);
useEffect(() => {
_getHistory();
}, []);
const expandMonth = (month,year) => {
console.log(month);
console.log(year);
}
const _getHistory = async () => {
let star = await AsyncStorage.getItem("star");
star = JSON.parse(star);
setStar(star);
var formData = new FormData();
formData.append("StarID", star.StarID);
Axios({
method: "post",
url: "**API URL**",
data: formData,
//headers: { "Content-Type": "multipart/form-data" },
})
.then(async (response) => {
var responseBody = response.data.detail;
setPeople(responseBody);
setLoading(false);
})
.catch((error) => {
console.log({ error });
});
};
if (loading)
return (
<SafeAreaView>
</SafeAreaView>
);
return (
<SafeAreaView
style={{
flex: 1,
backgroundColor: Colors.lightGrey,
paddingTop: Platform.OS === "android" ? 40 : 40,
justifyContent: "space-evenly",
}}
>
<Text style={styles.headerText}>History</Text>
<FlatList
//keyExtractor={(item) => item.id}
data={people}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => {
expandMonth(item.Month,item.Year)
navigation.navigate("HistoryMonthScreen", {
Month: item.Month,
Year: item.Year
});
}}
style={styles.month}>
<Text style={styles.monthname}>{moment(item.Month, 'M').format('MMMM')} {item.Year}</Text>
<Text style={styles.monthcount}>{item.Count} Bookings</Text>
</TouchableOpacity>
)}
/>
</SafeAreaView>
);
}
However when clicking the TouchableOpacity object it doesn't navigate to the next page passing the year and month variables. I assume I need to call some form of navigation to it, however everything I have tried has resulted in error after error.
For React Navigation 5 you have to use the route prop that is passed in alongside navigation for whatever screen you're navigating to.
Put the below code into HistoryMonthScreen.js
const HistoryMonthScreen = ({route, navigation}) => {
console.log(route.params.Month, route.params.Year);
return <View />
}
Edit: For context:
I'm going off what you have here
navigation.navigate("HistoryMonthScreen", {
Month: item.Month,
Year: item.Year
});
So I'm assuming you have some React Navigator somewhere that looks like this:
const AppNavigator5 = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="HistoryScreen"
screenOptions={{
headerShown: false,
animationEnabled: false,
}}>
<Stack.Screen
name="HistoryScreen"
component={HistoryScreen}
/>
<Stack.Screen
name="HistoryMonthScreen"
component={HistoryMonthScreen}
/>
<Stack.Screen name="Egg" component={Egg} />
</Stack.Navigator>
</NavigationContainer>
);
};
You put the code with route and navigation inside that HistoryMonthScreen component. You can pass different params into route.params every time, you don't have to change HistoryMonthScreen.
i am trying to build a react-native app in that user can add a routine or daily task in EditRoutine.js file and it can be seen in RoutineOverviewScreen.js and i am using redux for storing these data and using hooks for storing and fetching data.
Below is the EditRoutine.js code snippet
import React, { useState, useEffect, useCallback } from "react";
import { View, StyleSheet, Text, TextInput, ScrollView } from "react-native";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import Card from "../components/Card";
import { useSelector, useDispatch } from "react-redux";
import * as routinesActions from "../store/actions/routine";
import Routine from "../models/routine";
import HeaderButton from "../components/HeaderButton";
const EditRoutine = (props) => {
const dispatch = useDispatch();
const [title, setTitle] = useState("");
const [detail, setDetail] = useState("");
const [time, setTime] = useState("");
const submitHandler = useCallback(() => {
dispatch(routinesActions.createRoutine(title, detail, time));
props.navigation.goBack();
}, [dispatch,title, detail, time]);
useEffect(() => {
props.navigation.setParams({ submit: submitHandler });
}, [submitHandler]);
return (
<Card style={styles.container}>
<Text>Title</Text>
<TextInput
style={styles.input}
value={title}
onChangeText={(text) => setTitle(text)}
/>
<Text>Details</Text>
<TextInput
style={styles.input}
multiline
numberOfLines={4}
value={detail}
onChangeText={(text) => setDetail(text)}
/>
<Text>Time</Text>
<TextInput
style={styles.input}
value={time}
onChangeText={(text) => setTime(text)}
/>
</Card>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
padding: 10,
width: "100%",
},
input: {
paddingHorizontal: 2,
borderBottomColor: "#ccc",
borderBottomWidth: 1,
width: "100%",
marginVertical: 15,
},
});
EditRoutine.navigationOptions = (navData) => {
const submitFn = navData.navigation.getParam("submit");
return {
headerTitle: "Edit Routine",
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={
Platform.OS === "android" ? "md-arrow-back" : "ios-arrow-back"
}
onPress={() => {
navData.navigation.goBack();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === "android" ? "md-checkmark" : "ios-checkmark"
}
onPress={submitFn}
/>
</HeaderButtons>
),
};
};
export default EditRoutine;
and this is my RoutineOverviewScreen.js file where i am trying to show the created routine
import React from "react";
import { View, StyleSheet, Text, FlatList } from "react-native";
import Card from "../components/Card";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import HeaderButton from "../components/HeaderButton";
import { useSelector } from "react-redux";
const RoutineOverViewScreen = (props) => {
const routines = useSelector((state) => state.routines.myRoutine);
return (
<FlatList
data={routines}
keyExtractor={(item) => item.id}
renderItem={(itemData) => (
<Card>
<View>
<Text>{itemData.item.id} </Text>
</View>
<View>
<Text>{itemData.item.title} </Text>
</View>
<View>
<Text>{itemData.item.detail} </Text>
<View>
<Text>{itemData.item.time} </Text>
</View>
</View>
</Card>
)}
/>
);
};
const styles = StyleSheet.create({});
RoutineOverViewScreen.navigationOptions = (navData) => {
return {
headerTitle: "Your Routines",
headerLeft: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Menu"
iconName={Platform.OS === "android" ? "md-menu" : "ios-menu"}
onPress={() => {
navData.navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Add"
iconName={
Platform.OS === "android" ? "md-add-circle" : "ios-add-circle"
}
onPress={() => {
navData.navigation.navigate("Edit");
}}
/>
</HeaderButtons>
),
};
};
export default RoutineOverViewScreen;
Below is my action file routine.js snippet
export const CREATE_ROUTINE= 'CREATE_ROUTINE';
export const deleteRoutine = routineId => {
return { type: DELETE_ROUTINE, pid: routineId };
};
export const createRoutine = (title, detail, time) => {
return {
type: CREATE_ROUTINE,
routineData: {
title,
detail,
time
}
};
};
Below is my reducer file reducer.js snippet
import {
DELETE_ROUTINE,
CREATE_ROUTINE,
UPDATE_ROUTINE,
} from "../actions/routine";
import Routine from "../../models/routine";
const initialState = {
myRoutine: {},
id: 1,
};
export default (state = initialState, action) => {
switch (action.type) {
case CREATE_ROUTINE:
const newRoutine = new Routine(
state.id,
action.routineData.title,
action.routineData.detail,
action.routineData.time
);
return {
...state,
items: { ...state.items, [state.id]: newRoutine },
id: state.id + 1,
};
default: {
return state;
}
}
return state;
};
and this is my app.js file snippet
import React, { useState } from 'react';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AppLoading } from 'expo';
import * as Font from 'expo-font';
import routinesReducer from './store/reducers/routine';
import AppNavigator from './navigator/RoutineNavigator';
const rootReducer = combineReducers({
routines: routinesReducer,
});
const store = createStore(rootReducer);
const fetchFonts = () => {
return Font.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf')
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => {
setFontLoaded(true);
}}
/>
);
}
return (
<Provider store={store}>
<AppNavigator />
</Provider>
);
}
with these above code i am able to create a new routine but i am not knowing whether my input are getting stored in to the App central state because when i am trying to render those saved data i am unable to see in my RoutineOverviewScreen screen.please help me and
About Me: Govind Kumar Thakur ( iamgovindthakur )
email: iamgovindthakur#gmail.com
Thank You :)
You are trying to use the data of "myRoutine" but never save data to this property in the reducer.
I think that the issue you are having is due to the following line in your reducer:
items: { ...state.items, [state.id]: newRoutine },
Let's say I create a login screen. Inside that screen, I import form component. And inside form component I have a text input.
Then, I want to simulate text input on change text, but always get an error
Method “simulate” is meant to be run on 1 node. 0 found instead.
This is my test file
it('calls the login submit method', () => {
const fieldPhoneNumber = wrapper
.find('Form')
.dive()
.find('TextInput[id="fieldPhoneNumber"]');
fieldPhoneNumber
.at(0)
.simulate('changeText', { target: { value: '082262366193' } });
});
This is my component login file
import React, { useState, useEffect } from 'react';
import { ScrollView, StatusBar, Platform } from 'react-native';
import Header from './components/Header';
import Form from './components/Form';
import ButtonSocialMedia from './components/ButtonSocialMedia';
function LoginScreen() {
const [phoneNumber, setPhoneNumber] = useState('');
const [focus, setFocus] = useState(false);
useEffect(() => {
}, [phoneNumber]);
const changePhoneNumber = (value) => {
setPhoneNumber(value);
};
const showAppleButton = () => {
if (Platform.OS === 'ios') {
const version = Platform.Version.split('.')[0];
if (version >= 13) {
return true;
} else {
return false;
}
} else {
return false;
}
};
const loginSubmit = () => {
console.log('Login Submit');
};
return (
<ScrollView>
<StatusBar
translucent
backgroundColor="transparent"
barStyle="light-content"
/>
<Header />
<Form
phoneNumber={phoneNumber}
changePhoneNumber={(value) => changePhoneNumber(value)}
focus={focus}
setFocus={() => setFocus(true)}
loginSubmit={() => loginSubmit()} />
<ButtonSocialMedia showAppleButton={() => showAppleButton()} />
</ScrollView>
);
}
export default LoginScreen;
This is my form component
/* eslint-disable prettier/prettier */
import React from 'react';
import { View, Text, TextInput } from 'react-native';
import styles from '../styles/StyleForm';
import color from '../../../../__global__/styles/themes/colorThemes';
import regex from '../../../../constant/regex';
import * as yup from 'yup';
import { Formik } from 'formik';
import ButtonFull from '../../../../__global__/button/buttonFull';
const regexPhoneNumber = regex.phone;
function Form(props) {
const renderFocus = () => {
if (props.focus) {
return (
<Text style={styles.textFocus}>Type your phone number</Text>
);
}
};
return (
<Formik
enableReinitialize={true}
initialValues={{
phoneNumber: props.phoneNumber,
}}
onSubmit={values => {
console.log('Login Submit');
}}
validateOnMount={true}
validationSchema={yup.object().shape({
phoneNumber: yup
.string()
.required()
.min(8)
.matches(regexPhoneNumber, 'Phone number is not valid'),
})}>
{({
// values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit,
}) => (
<View style={styles.form}>
<View style={styles.subContainer}>
<View style={styles.containerTitle}>
<Text style={styles.textTitle}>+62</Text>
</View>
<View style={styles.containerPhoneNumber}>
{renderFocus()}
<TextInput
id={'fieldPhoneNumber'}
onFocus={() => props.setFocus(true)}
value={props.phoneNumber}
style={styles.subContainerPhoneNumber}
placeholderStyle={styles.placeholder}
placeholder={'Type your phone number'}
onChangeText={(value) => {
handleChange('phoneNumber');
props.changePhoneNumber(value);
setFieldTouched('phoneNumber', true);
}}
keyboardType={'numeric'}
onBlur={() => setFieldTouched('phoneNumber', true)}
/>
</View>
</View>
{touched.phoneNumber && errors.phoneNumber && (
<View style={styles.containerError}>
<Text style={styles.textError}>Phone number is not valid</Text>
</View>
)}
<View style={styles.containerButton}>
<ButtonFull
isDisabled={!isValid}
id={'buttonLogin'}
color={isValid ? color.thema : color.grey}
handleSubmit={() => props.loginSubmit()}
title={'Next'}
/>
</View>
</View>
)}
</Formik>
);
}
export default Form;
The error you're facing implies that the statement const fieldPhoneNumber wrapper.find('Form').dive().find('TextInput[id="fieldPhoneNumber"]'); couldn't find the TextInput component and hence the simulate function cannot be called. Try searching for the string "TextInput" inside the wrapper, and see if that works.