Redux Form + React Native Elements firing validation too soon - javascript

I'm using redux-form in React Native, along with React Native Elements to create forms.
The problem is, after the screen is rendered, the validations are getting called before the user has even had a chance to touch the input fields, showing input validations error too early.
My code:
FormInput:
import React from 'react'
import { Input } from "react-native-elements";
export default FormInput = (props) => {
const { input, meta: { error }, ...inputProps } = props
return <Input {...inputProps}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocues={input.onFocus}
value={input.value}
errorMessage={error}
/>
}
LoginForm:
import React from 'react';
import { reduxForm, Field, isDirty } from 'redux-form/immutable';
import { View, Image } from 'react-native';
import { Button } from "react-native-elements";
import { required, email } from 'redux-form-validators'
import FormInput from "./FormInput";
import styles from "./Styles/LoginFormStyles";
import Colors from '../Themes/Colors'
const bla = (value) => {
console.tron.log('value = ' + value)
return value !== undefined ? undefined : 'is required'
}
const LoginForm = (props) => {
const { handleSubmit, submitting } = props
return (
<View style={styles.container}>
<Image source={require('../Images/logo.png')} style={styles.logoContainer} resizeMode='center' />
<View style={styles.buttonHolderContainer}>
<Field name="email" validate={bla} textContentType="emailAddress" keyboardType="email-address" autoCapitalize = 'none' component={FormInput} placeholder="Email" containerStyle={styles.inputContainerHolder} />
<Field name="password" validate={[required()]} secureTextEntry component={FormInput} placeholder="Senha" containerStyle={styles.inputContainerHolder} />
<Button title="Entrar" style={{ marginTop: 10 }} buttonStyle={{ backgroundColor: Colors.appColor }} titleStyle={{ color: 'black' }}
onPress={handleSubmit} loading={submitting} disabled={submitting}/>
</View>
</View>
)
}
export default reduxForm({ form: 'LoginForm'})(LoginForm)
Versions:
"react-native": "0.57.7",
"react-native-elements": "^1.0.0",
"react-navigation": "3.1.3",
"react-redux": "^5.0.6",
"redux": "^4.0.0",
"redux-form": "^8.1.0"
The Redux call chain:
As you can see, the UPDATE_SYNC_ERRORS event is getting dispatched before any user interactions to the input.
Thanks for your time!

I think you may want to look at using the helper function shouldError() so you can control when redux-form chooses to perform its validation.
Although the code example is out-of-date because it uses the deprecated shouldValidate() you can still get an idea of how to use shouldError() by taking a look at this SO answer.

Inside of meta you have properties like touched, visited etc. You can use those property to show error based on condition:
meta.touched: true if the field has been touched. By default this will be set when the field is blurred.
meta.visited: true if this field has ever had focus. It will only work if you are passing onFocus to your input element.
So the errorField can be:
errorMessage={meta.touched && error}

Related

How to save TextInput with it's defaultValue if the user hasn't changed anything?

Okay so I hope this is easy, but I can't find anything about that on Google.
I have a details screen for a (cooking)receipe in my app. With the tap of a button the user can set a state isEditing, which then converts a heading into a text input. That heading displays {receipe.title} and I use the same value for the default value prop on the text input.
Once the user taps the edit button again, isEditing will be set to false and a update function will update the receipe in my Firebase database.
{!isEditing ? (
<Text style={styles.headingLarge}>{receipes.title}</Text>
):(
<TextInput
placeholder='Titel'
autoFocus={true}
defaultValue={receipes.title}
style={styles.headingLarge}
onChangeText={text => {
setPresentTitle(text);
}}/>
)}
It's all working, as long as the user actually changes something in the input. But the issue is that if a user doesn't change anything, onChangeText is never called and the database is updated with an empty string as it's title.
Is there a way to call the onChangeText when setting the defaultValue for this input (or another hack to set the setPresentTitle)?
Hey you can check this snack out, ive made it for you
This is the snack : https://snack.expo.dev/#gaurav1995/fascinated-donut
Hope it helps. feel free for doubts
import React,{ useState ,useEffect } from 'react';
import { StyleSheet, Text, TouchableOpacity, View ,TextInput ,Button } from 'react-native';
export default function App() {
const receipes = {
title:"Hey there"
}
const [isEditing, setEd] = useState(false)
const [text,setPresentTitle] = useState(receipes.title)
const toggleEdit = () => {
setEd(!isEditing)
}
useEffect(() => {
//update to firebase
//setFirebase(text)
},[text])
return (
<View>
{!isEditing ? (
<Text style={styles.headingLarge}>{text}</Text>
):(
<TextInput
placeholder='Titel'
autoFocus={true}
value={text}
style={styles.headingLarge}
onChangeText={text => {
setPresentTitle(text);
}}/>
)}
<Button title="Toggle Edit" onPress={toggleEdit} containerStyle={{marginTop:20}} />
</View>
)
}
const styles = StyleSheet.create({
container:{
flex:1,
padding:40
},
headingLarge:{
fontSize:40,
marginBottom:20
}
})

Fixing React Native CheckBox

I have a React Native project. My understanding is that react native doesn't allow you to style checkboxes inherently, so I am using react-native-check-box and looking on expo.
When running I get "Unidentified is not an object (evaluating 'this.state.isChecked')"
I am using the exact suggested code from https://www.npmjs.com/package/react-native-check-box#demo
What is going wrong?
import React, { useState } from 'react';
import { Text, View, Image } from 'react-native';
import CheckBox from 'react-native-check-box';
import defaultStyles from "../../config/styles";
function AuthorizeInput () {
return (
<View style={defaultStyles.authorize}>
<CheckBox
style={{flex: 1, padding: 10}}
onClick={()=>{
this.setState({
isChecked:!this.state.isChecked
})
}}
isChecked={this.state.isChecked}
/>
<Text style={defaultStyles.authText}>I am an authorized representative of this business.</Text>
</View>
);
}
export default AuthorizeInput;
With functional components u can't use this.setState (only with class ones). However you can use useState hook. For example:
import React, { useState } from "react";
import { Text, View, Image } from "react-native";
import CheckBox from "react-native-check-box";
import defaultStyles from "../../config/styles";
function AuthorizeInput() {
// default value is false
const [checked, setChecked] = useState(false);
return (
<View style={defaultStyles.authorize}>
<CheckBox
style={{ flex: 1, padding: 10 }}
onClick={() => setChecked(!checked)}
isChecked={checked}
/>
<Text style={defaultStyles.authText}>
I am an authorized representative of this business.
</Text>
</View>
);
}
export default AuthorizeInput;

How to use custom alert in React Native?

I've created a custom alert as a component in React Native. I used Modal to create this custom alert. My problem is, how to use it? Instead of using Alert.alert in React Native, I want to display my own alert.
Here is my Custom Alert Modal.
import React, { Component } from 'react';
import { Text, View } from 'react-native';
import Modal from 'react-native-modal';
import styles from './style';
import Button from '../../components/Button';
export default class CustomAlert extends Component {
renderModalContent = () => (
<View style={styles.content}>
<Text style={styles.contentTitle}>{this.props.title}</Text>
<Text style={styles.contentInfo}>{this.props.content}</Text>
<View style={styles.buttonContainer}>
<Button
color={this.props.buttonColor}
text={this.props.buttonText}
onPress={this.props.buttonOnPress}
/>
</View>
</View>
);
render() {
return (
<View style={styles.container}>
<Modal
isVisible={this.props.isVisible}
backdropColor="#000000"
backdropOpacity={0.9}
animationIn="zoomInDown"
animationOut="zoomOutUp"
animationInTiming={600}
animationOutTiming={600}
backdropTransitionInTiming={600}
backdropTransitionOutTiming={600}
>
{this.renderModalContent()}
</Modal>
</View>
);
}
}
This is how I want to use it. I want to display this alert through a function. That means when function catches up with an error, I want to show my custom alert.
myFunction()
.then(() => // do something)
.catch(() => // show my custom alert);
Can you help me please to solve this problem?
set isVisible true in your catch block.
myFunction()
.then(() => // do something)
.catch((e) => this.setState({ isVisible: true }))

Formik and Material-UI

I am trying to use Formik with Material-UI text field. Like so:
import TextField from '#material-ui/core/TextField';
import {
Field,
FieldProps,
Form,
Formik,
FormikErrors,
FormikProps
} from 'formik';
import React, { Component } from 'react';
interface IMyFormValues {
firstName: string;
}
class CreateAgreementForm extends Component<{}> {
public render() {
return (
<div>
<h1>My Example</h1>
<Formik
initialValues={{ firstName: '' }}
// tslint:disable-next-line:jsx-no-lambda
onSubmit={(values: IMyFormValues) => alert(JSON.stringify(values))}
// tslint:disable-next-line:jsx-no-lambda
validate={(values: IMyFormValues) => {
const errors: FormikErrors<IMyFormValues> = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
return errors;
}}
// tslint:disable-next-line:jsx-no-lambda
render={(formikBag: FormikProps<IMyFormValues>) => (
<Form>
<Field
name="firstName"
render={({ field, form }: FieldProps<IMyFormValues>) => (
<TextField
error={Boolean(
form.errors.firstName && form.touched.firstName
)}
helperText={
form.errors.firstName &&
form.touched.firstName &&
String(form.errors.firstName)
}
/>
)}
/>
</Form>
)}
/>
</div>
);
}
}
export default CreateAgreementForm;
I want Formik to be responsible for validation and Material-UI for looks.
I want to pass errors.firstName to TextField component but the error doesn't display correctly. How can I fix it so it still will be clear to read? I don't want to write my own TextField component.
I don't think you need another library or even create your own wrapper, I think you need to tweek your code a bit.
One problem you have is that you don't pass an onChange function in the Material TextField so the form value of firstName is always null and so you always get the error, even if you have entered a name.
Try adding a name or id on your TextField and an onChange function like so:
<Field
validateOnBlur
validateOnChange
name="firstName"
render={({ field, form }) => (
<TextField
name={"firstName"}
error={
Boolean(form.errors.firstName && form.touched.firstName)
}
onChange={formikBag.handleChange}
onBlur={formikBag.handleBlur}
helperText={
form.errors.firstName &&
form.touched.firstName &&
String(form.errors.firstName)
}
/>
)}
/>
As mentionned in comments, it may actually be a good idea to implement "wrapper" components, like they did in this samples from Formik or ReactFinalForm :
https://github.com/stackworx/formik-material-ui/tree/master/src
https://github.com/final-form/react-final-form#material-ui-10
The idea is the same : implement custom "wrapper" components to wrap Material-UI components and map Formik or ReactFinalForm APIs props.
The advantages of this approach is to centralize in one place the mapping between the two frameworks, so that you do not repeat the mapping each time, and if one of the framework introduces breaking changes you just have to change those custom "wrapper" components.
You can try this: https://github.com/daixianceng/formik-material-fields
Installation:
npm install --save formik-material-fields
Usage:
import React, { Component } from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import { FormikTextField } from 'formik-material-fields';
const validationSchema = Yup.object().shape({
username: Yup.string().required(),
});
const initialValues = {
username: '',
};
class MyForm extends Component {
render() {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={this.props.onSubmit}
>
{({ isValid }) => (
<Form autoComplete="off">
<FormikTextField
name="username"
label="Username"
margin="normal"
fullWidth
/>
</Form>
)}
</Formik>
);
}
}
Check out the formik docs for <Field /> here: https://jaredpalmer.com/formik/docs/api/field
As an example you could use Material's OutlinedInput to style your input:
<Field as={OutlinedInput} />
If you need to pass more props to OutlinedInput, simply add them to Field and it will pass them on to OutlinedInput:
<Field as={OutlinedInput} color="primary" InputProps={{ startAdornment: <InputAdornment position="start"><AccountCircle /></InputAdornment> }} />
You can use setFieldValue method useful for creating custom input change handlers
<Formik
initialValues={{
name: "",
}}
onSubmit={(values: any) => console.log(values)}
>
{({ handleSubmit, setFieldValue }) => (
<Form noValidate autoComplete="off" onSubmit={handleSubmit}>
<TextField
onChange={(event) => setFieldValue("name", event.target.value)}
type="text"
label="Name"
/>
</Form>
)}
</Formik>
To use material-ui and formik, you can use the variant from the official formik documentation:
https://formik.org/docs/examples/with-material-ui
You could also try this library, which does the heavy-lifting for you and implements the wrapper code around Material-UI components (including <TextField />): https://github.com/stackworx/formik-material-ui.
Installation:
yarn add formik-material-ui
In your Formik form component, pass the <TextField /> component as the component prop of the Formik <Field /> component.
import { Formik, Field, Form } from 'formik';
import { TextField } from 'formik-material-ui';
<Field
name="email"
label="Email"
type="email"
component={TextField}
/>
Formik will continue to handle the validation as expected and will render the Material UI component and error message. There are additional details in the docs for other Mui input components and to aid with customization.

redux-form Cannot read property 'any' of undefined

I have problem with redux-from in react-native. I want to implement a login form for my app. from tutorials around the web I wrote this code but have problem with this error
Cannot read property 'any' of undefined
Package.json
"dependencies": {
"react": "16.2.0",
"react-addons-update": "^15.6.2",
"react-native": "0.53.0",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-form": "^5.3.2",
"redux-persist": "^3.2.2"
},
this is my Login.js component
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native';
import { reduxForm } from "redux-form";
class Login extends Component {
onSignIn() {
var { email, password } = this.props.fields;
console.log(email.value, password.value);
}
render(){
var {fields: {email, password}} = this.props;
return (
<View style={styles.container}>
<View style={styles.titleContainer} >
<Text style={styles.title} >ToDo</Text>
</View>
<View style={styles.fields} >
<TextInput
{...email}
placeholder="Email"
style={styles.TextInput}
/>
</View>
<View style={styles.fields} >
<TextInput
{...password}
placeholder="Password"
style={styles.TextInput}
/>
</View>
<View style={styles.buttonContainer} >
<TouchableOpacity>
<Text style={styles.button} onPress={this.onSignIn} >
Signin
</Text>
</TouchableOpacity>
<TouchableOpacity>
<Text style={styles.button}>
Signup
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
...
});
var validate = (formProps) => {
var errors = {};
return errors;
}
export default reduxForm({
form: 'login',
fields: ['email', 'password'],
validate: validate
}, null, null)(Login);
my reducer index.js
import update from "react-addons-update";
import { combineReducers } from "redux";
import { reducer as formReduser } from "redux-form";
module.exports = combineReducers({
form: formReduser
})
Looks like that version of redux-form doesn't support react 16. Prop types have been moved to a separate package. Try upgrading redux-form to 5.3.6 or greater.

Categories