React-Native (0.64.2) Formik handleSubmit onSubmit not firing - javascript

Relevant Modules:
Formik: "2.1.4"
Yup: "0.28.5"
React-Navigation v5
So i've been struggling with a simple formik problem for a day now, and I'm sure I must be missing something, but I would love some help, because I can't see what I'm doing wrong.
I have two files: Login/index.tsx & Login/LoginForm.tsx
Login/index.tsx for the most part looks like this:
const handleSubmit = async (values) => {
console.log("values submitted == ", values)
}
return (
<View style={Styles.container}>
<CircleIconButton
size={60}
backgroundColor={'white'}
onPress={goBack}
>
<Icon name={'arrow-left'} type={'FontAwesome5'} style={{ color: Colors.primaryOrange}} />
</CircleIconButton>
<Text style={[Typography.title, {color: 'white', marginTop: '30%'}]}>
Login
</Text>
<Text style={[Typography.titleDesc, {color: 'white', marginTop: '3%', marginBottom: '8%'}]}>
Securely login to your account
</Text>
<Formik
initialValues={formInitialValues}
onSubmit={handleSubmit}
>
<LoginForm />
</Formik>
</View>
)
Login/LoginForm.tsx for the most part looks like this:
const {handleChange, handleSubmit, values, isSubmitting, isValidating} = useFormikContext();
const { navigate } = useNavigation()
return (
<View style={Styles.container}>
<IVTextInput
onChangeText={handleChange('email')}
value={values?.email}
editable
fieldLabel={"Phone/Email"}
/>
<IVTextInput
isPassword
editable
containerStyle={{ marginTop: '8%', marginBottom: '15%' }}
onChangeText={handleChange('password')}
value={values?.password}
fieldLabel={"Your Password"}
/>
<RoundedButton
block
onPress={handleSubmit}
>
Log In
</RoundedButton>
<View style={{...allCenter, marginTop: '7%'}}>
<Text
style={[Typography.titleDesc, Styles.signupTextButton]}
onPress={() => navigate('Signup')}
>
Don’t have an account? Register
</Text>
<Text style={[Typography.titleDesc, {color: 'white', marginTop: '8%'}]}>
Forgot Password
</Text>
</View>
</View>
)
LoginForm is wrapped by the <Formik> tags. I then try to access the formik context in the LoginForm.tsx file.
Things i've tried:
Printing the values variables as it changes (from me typing in the textfields), and I can see that the values object is tracking my keystrokes
Printing isSubmitting & isValidating, and at no point has isSubmitting = true AND isValidating = false. when I submit isSubmitting == true and isValidating == true. The docs for Formik seem to suggest that I know when my form is submitting when isValidating == false && isSubmitting == true but that hasn't happened yet.
Creating a Yup schema, but i've also tried removing the yup schema within the <Formik> tag inside the Login/index.tsx file, either way, neither the onSubmit nor the handleSubmit funciton do anything when I tap the Login button.
Put in a simple console.log() statement to test that the action to my <RoundedButton> onPress prop is working, and it worked, my console.log() was seen in the console output.
Returning an empty object from the validate prop within the <Formik> opening tag, because the general consensus I get from looking online is that the form doesn't submit when the validation fails
Interesting Info: When I used a validationSchema, I purposely violated the validation, and when I printed errors the object was empty
The gist is . . . nothing i've tried seems to get the onSubmit or
handleSubmit functions to fire. Please let me know what I could be
doing wrong, any help would be greatly appreciated.
Thank You

Related

React Native auth error handle with <View> component

i'm handling firebase auth errors and i cant show error message on screen
here is my code;
if (error.code === "auth/user-not-found") {
return (
<View>
<Text>user not found</Text>
<Text>try create a account or check ur mail and passwrd</Text>
</View>
)
}
but i use alert it works perfectly.
how can i solve it ?
Try to add some styles in your components so you can see something.
if (error.code === "auth/user-not-found") {
return (
<View style={{flex: 1, width: "100%", height: "100%", backgroundColor: "#f00"}}>
<Text>user not found</Text>
<Text>try create a account or check ur mail and passwrd</Text>
</View>
)
}
I like to try first with full width and height, and with a very basic color (in this case red) so I can watch whats happening in my views. Remember that you can save and metro gonna refresh your app in your emulator/phone.

onChangeText callback function save previous input

iam new in react native
and iam trying to create my first todo list app using react native and js
i faced a problem in validation of this app
iam using a textInput to take the task as input from the user and iam using onchangetext callback function
<TextInput style={styles.input} placeholder={'Write a task'} value={task} onChangeText={text =>{
console.log(text.length)
if(text.length==0){
checkEmpty(true);
setTask(null);
}
else{
checkEmpty(false);
setTask(text);
clearText('');
}
}}/>
and a buttom to create the task
<TouchableOpacity onPress={()=>{
handleAddTask();
}}>
<View style={styles.addWrapper}>
<Text style={styles.addText}>+</Text>
</View>
</TouchableOpacity>
and this is the function handleAddTask
const handleAddTask=()=>{
Keyboard.dismiss();
if(isEmpty==false){
setTaskItems([...taskItems,task])
setTask('');
}
else{
Alert.alert('OOPS!','Todos must contain at least 1 character',[
{text:'Okay', onPress: ()=> console.log('Alert closed')}
]);
setTask('');
}
}
the problem here is that
after the application start for the first time : if i didnt enter any input and press on the TouchableOpacity the alert pop up
and after i enter any input and add a task and add it successfully when i press the TouchableOpacity again when the input is empty it create empty task to solve the problem i must type any character and delete it and press the TouchableOpacity to make the alert pop up again as the input is empty
... i want to know how to solve this validation problem
I tried playing along with your code and I think the isEmpty state isn't working as expected (you didn't expose that part of code either). You can update your checkEmpty after task is updated.
useEffect(() => {
if(task.length) {
checkEmpty(false)
} else {
checkEmpty(true)
}
},[task])
But ,actually you don't need to assign another state to check if the state is empty, you can just use task.length to check.
const [task, setTask] = React.useState('');
const [taskItems, setTaskItems] = React.useState([])
const handleAddTask=()=>{
// Keyboard.dismiss();
if(task){
setTaskItems(prev => [...prev,task])
setTask('');
}
else{
Alert.alert('OOPS!','Todos must contain at least 1 character',[
{text:'Okay', onPress: ()=> console.log('Alert closed')}
]);
// setTask('');
}
}
return (
<View style={styles.container}>
<TextInput
placeholder={'Write a task'}
value={task}
onChangeText={(text) => setTask(text)}
/>
<TouchableOpacity
onPress={() => {
handleAddTask();
}}>
<View style={{margin : 5}}>
<Text style={{fontSize: 24}}>+</Text>
</View>
</TouchableOpacity>
{taskItems.length ? taskItems.map(t=> <Text style={styles.paragraph}>{t}</Text>) : undefined}
</View>
);
Check on - snack.expo

How to pass different event handlers for the same event?

I have a react-native component which is rendering 2 Icons for marking a favorite and writing a comment as shown below:
function RenderDish(props) {
const dish = props.dish;
if (dish != null) {
return (
<Card featuredTitle={dish.name} image={{ uri: baseUrl + dish.image }}>
<Text style={{ margin: 10 }}>
{dish.description}
</Text>
<View style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
<Icon raised reverse name={props.favorite ? 'heart' : 'heart-o'} type='font-awesome' color='#f50'
onPress={() => props.favorite ? console.log('Aleady Favorite') : props.onFavorite()} />
<Icon raised reverse name='pencil' type='font-awesome' color='#3b5998'
onPress={() => props.onComment()} />
</View>
</Card>
);
}
else {
return (<View></View>);
}}
I am calling this functional component from the outer component as shown below:
<RenderDish dish={this.props.dishes.dishes[+dishId]}
favorite={this.props.favorites.some(el => el === dishId)}
onFavorite={() => this.markFavorite(dishId)}
onComment={() => this.toggleModal()} />
I have already implemented the toggleModal() and the markFavorite() methods and everything is working as expected but my question is: Is there any other way of passing 2 or more different event handlers through a single prop ? For eg. Is there any way to say something like: <RenderDish dish={xyz} onPress={()=> handler1 AND handler2}. Or is there any elegant alternative to what I have done(if I had 5 buttons I would need 5 props :( ) ?
You can have your handlers in a single function and call the function in onPress.
or just
onPress={()=> {handler1, handler2}}
Try something like below. Pass the handler methods as an object in a props.
function Button(props) {
return (
<div>
<button type="button" onClick={props.onPress.handler1} >Handler1</button>
<button type="button" onClick={props.onPress.handler2} >Handler2</button>
</div>
);
}
class Home extends Component {
handler1() {
console.log('handler 1');
}
handler2() {
console.log('handler 2');
}
render() {
return (
<div>
<Button onPress={{ handler1: this.handler1, handler2: this.handler2 }} />
</div>
)
}
}
The way that you’ve done it is probably the most common way and perfectly acceptable.
It’s not generally a problem if there are only a few handlers (and they aren’t being passed deeply).
You could bundle them into an object and single prop, but I don’t typically see it done that way and I don’t think there’s a strong benefit.
You can also pass in multiple props as a single spread object without having to bundle them into a single prop.
If you start getting a more handlers than you’re comfortable with, then that would probably be a good time to look at how you’re handling your state management. You may be better off with a reducer and dispatching actions at that point.
Update
A few other notes about your code:
You can destructure props for easier use.
You do not need a separate closing tag for View if there is no content.
You do not have to make new arrow functions for event handlers if all you are doing is calling another arrow function. You can just set it to the first arrow function.
Consider a ternary to allow an implicit return.
function RenderDish({dish, favorite, onFavorite, onComment}) =>
dish
? <Card featuredTitle={dish.name} image={{ uri: baseUrl + dish.image }}>
<Text style={{ margin: 10 }}>
{dish.description}
</Text>
<View style={{ display: "flex", flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
<Icon raised reverse name={favorite ? 'heart' : 'heart-o'} type='font-awesome' color='#f50'
onPress={() => favorite ? console.log('Aleady Favorite') : onFavorite()} />
<Icon raised reverse name='pencil' type='font-awesome' color='#3b5998'
onPress={onComment} />
</View>
</Card>
: <View/>
I would focus more on these items, rather than bundling those two functions.

React Native FlatList keyboardShouldPersistTaps not persisting

I have a very frustrating situation. Trying to get keyboard to disappear and detect onPress event handler in child row.
Here is what my code looks like:
_renderRow = (prediction) => {
return (
<TouchableOpacity onPress={() => {
Keyboard.dismiss();
this.setState({ location: prediction.description });
}}>
<View style={styles.listItemContainer}>
<Text>{prediction.description}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
<View style={styles.wrapper}>
{/* style={[this.state.predictions.length > 0 ? styles.searchContainerSuggest : styles.searchContainer]} */}
<View style={styles.searchContainerSuggest}>
<View style={{paddingLeft: 10, height: 45, display: 'flex', justifyContent: 'center'}}>
<TextInput
placeholder="Enter location"
value={this.state.location}
onChangeText={location => this.onChangeLocation(location)}
style={styles.textInput}
/>
</View>
{this.state.predictions.length && this.state.location !== '' ?
<FlatList
keyboardShouldPersistTaps={'handled'}
refreshing={!this.state.loaded}
initialNumToRender={10}
enableEmptySections={true}
data={this.state.predictions}
keyExtractor={(_, index) => index.toString()}
renderItem={ ({item: prediction}) => this._renderRow(prediction) } />
: null}
</View>
</View>
);
}
I probably need a helping hand or two with regards to how to debug this issue.
Looked up several examples on how to deal with hiding the keyboard and allowing a particular selection to be pressed at the same time.
I thought that keyboardShouldPersistTaps would allow for the child selection to be selected. Upon selection, the onPress event handler will trigger and that will be where I call Keyboard.dismiss() to hide the keyboard. Does not seem to work.
In my case, besides adding keyboardShouldPersistTabs='handled' to the FlatList in question, it was also needed to add keyboardShouldPersistTabs='handled' and nestedScrollEnabled={true} to a parent ScrollView like 2 levels above, wrapping the FlatList I intended to get this behavior with. Check out this issue in react-native repo for more info.
For anyone who is running into the same problem as me. Check whether your FlatList or ScrollView is nested in another FlatList or ScrollView.
If yes, then add
keyboardShouldPersistTaps='handled'
to the element as a props as well.
add keyboardDismissMode="none" to FlatList

How to render Container Classes conditionally

firstly, this is what is given to me from designer http://www.giphy.com/gifs/hSRrqF5ObsbXH27V09
Basically, there is a category which is passed from previous screen. and with some ui interactions, i need to render this screen again and again. the flow is like that: you select a category, if it has subCategories, let user select one of those subCategories before rendering input components. i can make it work with if and else clauses but i feel that this is some how not best practice at all. I just need an advice from experieced developer(i am reletively new to react native.)
So before writing any code with native way, i just want to ask it here so maybe i can learn more about it.
Here is my Screen:
<NewAdHoc
contentText={'Kategori Secimi'}
onBackPress={this.handleBackPress}
showContentText={false}
>
<View style={styles.container}>
{currentCategory !== null
? (
<View style={{ ...styles.flatListContainer, paddingLeft: undefined, paddingRight: undefined }}>
<View style={{ marginHorizontal: 20 }}>
<ListViewItem
categoryName={currentCategory.categoryName}
iconName={currentCategory.categoryIcon}
showBorder={false}
key={currentCategory.categoryId}
categoryId={currentCategory.categoryId}
inNewAdScreen={false}
/>
</View>
<Seperator
backgroundColor={colors.SEPERATOR_BCK}
text={'Ilan Turu'}
style={{ paddingHorizontal: 20 }}
/>
{
currentCategory.subCategories.map((subc) => (
<View style={{ marginHorizontal: 20 }}>
<SubCategoryItem
text={subc.subCategoryName}
key={subc.subCategoryId}
showBorder={true}
/>
</View>
))
}
</View>
) : null}
</View>
</NewAdHoc>
right now, i am rendering a category, a <Seperator/> between category and subcategories, and subcategories. what i want is that, when user click on one of the subCategories, i will change the state to isSubCategorySelected = true, selectedSubCategory= subCategoryId and then need to render the whole screen like in gif i provided above.
the For those who came here for answer:
What i did is basically divide and conquer paradigm. I firstly divide my sitution into two main state.
RenderInitialForm(),
renderAfterSubCategorySelected(),
those two rendering functions handle the whole process. when a TouchableOpacity is clicked, i setState with two variables: isSubCategorySelected = true, selectedSubCategory = subCategory
and in my main render() function:
render() {
const { currentCategory, isSubCategorySelected, selectedSubCategory } = this.state
return (
<NewAdHoc
contentText={'Kategori Secimi'}
onBackPress={this.handleBackPress}
showContentText={false}
>
<View style={styles.container}>
{currentCategory !== null
? isSubCategorySelected
? this.renderAfterSubCategorySelected()
: this.renderInitialForm()
: null}
</View>
</NewAdHoc>
);
}
if you have any suggestion with my solution, please feel free to contact me.

Categories

Resources