react-native login screen - error when login button is pressed with empty login fields - react-native

I have a simple login screen that asks for an email and password.
Login Screen
If the "Sign In" button is pressed and both of the fields are blank I get this error: "null is not an object (evaluating'_this.state.Email')"
Error Screen
Here is the code:
import React, {Component} from 'react';
import {View, Button, ScrollView, AsyncStorage, Alert } from 'react-native';
import colors from '../config/colors';
import { TextInput } from '../components/TextInput';
class SignIn extends Component {
signIn = () => {
const {Email} = this.state;
const {Password} = this.state;
fetch('http://192.168.1.3/Restaurant_App/php/sign_in.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application.json',
},
body: JSON.stringify({
email: Email,
password: Password,
})
}).then((response) => response.json())
.then((responseJson) => {
if (responseJson == Email) {
Alert.alert(responseJson);
AsyncStorage.setItem('email', Email);
this.props.navigation.navigate('Search');
} else {
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
};
render() {
return (
<View>
<ScrollView style={{ backgroundColor: colors.background }}>
<TextInput
placeholder="Email..."
onChangeText={Email => this.setState({Email})}
/>
<TextInput
placeholder="Password..."
secureTextEntry={true}
onChangeText={Password => this.setState({Password})}
/>
</ScrollView>
<Button
onPress={() => this.signIn()}
title="Sign In"
/>
</View>
);
}
}
export default SignIn;
I would like it to be so that if the "Sign In" button is pressed with empty fields, I won't get this error. Instead, there should be an alert saying "Please fill in all fields." or something like that.

You should do some validation checks before making the fetch request.
You could do something like this
signIn = () => {
const {Email, Password} = this.state;
if(!this.checkDetails(Email, Password) {
// you could show an alert here, but it is not great UX,
// you should show your user where they have gone wrong,
// by making style changes, a red border around the TextInput,
// text explaining what has gone wrong.
return;
}
fetch('http://192.168.1.3/Restaurant_App/php/sign_in.php', {
...
}).then((response) => response.json())
.then((responseJson) => {
...
}).catch((error) => {
console.error(error);
});
};
checkDetails = (Email, Password) => {
// check that it is a valid email address
// this is a regex that I have used in the past to check email addresses.
const emailIsValid = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(Email);
// if the password has a minimum length then you should use that rather than 0
this.setState({emailIsValid, passwordIsValid: Password.length > 0});
if (emailIsValid && Password.length > 0) return true;
return false;
}
Using these new state values for the email and password being valid you could set additional styles and error text beside the fields that are wrong or missing.
<TextInput
placeholder="Email..."
onChangeText={Email => this.setState({Email})}
styles={this.state.emailIsValid ? styles.validEmail : styles.invalidEmail}
/>
{!this.state.emailIsValid && <Text>Please input a valid email</Text>}
<TextInput
placeholder="Password..."
secureTextEntry={true}
onChangeText={Password => this.setState({Password})}
styles={this.state.passwordIsValid ? styles.validPassword : styles.invalidPassword}
/>
{!this.state.passwordIsValid && <Text>Please input a valid password</Text>}
Don't for get to set up your styles for the different states.
const styles = StyleSheet.create({
validEmail: {},
validPassword: {},
invalidEmail: {},
invalidPassword: {}
});
You'll probably want to add initial state values for the emailIsValid and passwordIsValid so that they are set to true so that the correct styles are shown. Also you should define initial state for the Email and Password.
Add a constructor to your class
constructor (props) {
super(props);
this.state = {
Email: '',
Password: '',
emailIsValid: true,
passwordIsValid: true
}
}
I hope that this helps.

You can do at the top of your sign in function something like this:
If(this.state.email.length === 0 || this.state.password.length === 0) {
alert(“please complete the fields”);
return;}

Related

React Native Workflow, handle 429 erros and data

im looking for a bit of guideness here, im working on a RN app with redux and everytime i enter a new screen on the app, must likely i have a "callinitialData" function inside my useEffect(), using axios to fetch api data to be dispatch() to the redux state.
Everything works but whenever i jump from screen to screen to fast, sometimes i get a 429 error of to many request, so i just setup the redux-persist hoping that would help reduce the amount of request,in my mind thinking that if my api data is equal to my local data, that request wouldnt be necessary to be made.
However it stays the same so i was thinking what would be the best aproach here, on login try to fetch all the data at once > store it at asyncstorage and redux, and fetch that on each screen ?
how would i be able then, if i fetch all the data on login, receive the new data sets from the api in real time?
App functionality -
Edit Profile (img, pass, email, name)
Data Forms (requeast X, submit data, edit forms)
Chat by contacts / create Group chat
Code Example
const ChatScreen = ({ auth: { user }, getChatContacts, chat: { contacts }, navigation }) => {
useEffect(() => {
getChatContacts();
}, []);
const onChatUser = async (_id, name, roomID) => {
console.log(_id, name, roomID, contacts.payload.clone)
navigation.navigate( "Message", {
_id, name, chatRoomId: roomID, allUsers: contacts.payload.clone
});
}
const renderItem = ({ item , index } ) => {
let userName = "";
item.users.map((users, index) => {
let idToCheck = users.toString() !== user._id.toString() ? users : false;
if (idToCheck) {
let getOneUser = contacts.payload.clone.find(x => x._id === idToCheck);
userName += "" + getOneUser.name + ", ";
}
})
return (<TouchableOpacity key={item._id} onPress={() => onChatUser(item._id, item.name, item.roomID)}>
<View style={styles.chatContainer}>
<FontAwesome name="user-circle-o" size={50} color="#000000"/>
<Text style={styles.chatTitle}>{ ((userName).length > 32) ?
(((userName).substring(0,32-3)) + '...') :
userName }</Text>
<FontAwesome name="angle-right" size={25} color="#000000"/>
</View>
</TouchableOpacity>)
};
return (
<SafeAreaView style={styles.container}>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
placeholder="Search friend"
style={styles.chatsearch}
/>
{contacts ?
(<FlatList
data={contacts.payload.allContact}
renderItem={(item, index) => renderItem(item, index)}
keyExtractor={item => item.id}
style={styles.FlatListContainer}
/>) : (<Text style={styles.FlatListContainer}></Text>)
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({});
ChatScreen.propTypes = {
isAuthenticated: PropTypes.bool,
auth: PropTypes.object,
};
const mapStateProps = state => ({
auth: state.auth,
chat: state.chat
});
export default connect(mapStateProps, {getChatContacts} )(ChatScreen);
Redux Action
export const getChatContacts = () => async dispatch => {
const config = {
header: {
"Content-Type": "application/json"
}
}
try {
const res = await axios.get(API_LINK +"/users/getChatContacts",);
dispatch({
type: GET_CONTACT_CHAT,
payload: res.data
});
} catch (err){
console.log(err)
dispatch({
type: ERROR_FAMILY_PARENT,
payload: { msg: err.response, status: err.response}
});
}
};

Get save data in different page by async saveToStorage(userData)

I am creating a react native app and doing the login and profile page. I have used the "async saveToStorage(userData)" for save the user data. Now i want to get the same data in the profile page.
I want to use this
getData = async () => {
try {
const value = await AsyncStorage.getItem('#storage_Key')
if(value !== null) {
// value previously stored
}
} catch(e) {
// error reading value
}
}
But how to use this in my profile page to Show this.
I saved this in the login page
async saveToStorage(userData){
if (userData) {
await AsyncStorage.setItem('user', JSON.stringify({
isLoggedIn: true,
authToken: userData.auth_token,
id: userData.user_id,
name: userData.user_login
})
);
return true;
}
return false;
}
And in the Profile page i have to display the name only. So how can use that.
import AsyncStorage from '#react-native-community/async-storage';
export default class Profile extends Component {
constructor(props){
super(props)
this.state={
userEmail:'',
userPassword:'',
}
}
var uservalue = await AsyncStorage.getItem('user');
home() {
Actions.home()
}
render() {
return (
<View style={styles.container}>
<View style={styles.header}></View>
<Image style={styles.avatar} source={{uri: 'https://bootdey.com/img/Content/avatar/avatar6.png'}}/>
<View style={styles.body}>
<View style={styles.bodyContent}>
<Text style={styles.name}>Robert Vadra</Text>
<Text style={styles.info}>Total Token: 30 {uservalue.name}</Text>
<Text style={styles.description}>Lorem ipsum dolor sit amet, saepe sapientem eu nam. Qui ne assum electram expetendis, omittam deseruisse consequuntur ius an,</Text>
<TouchableOpacity style={styles.buttonContainer} onPress={this.home} >
<Text style={styles.buttonText}>Play Now</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
In the place of "Robert Vadra", i want to display the stored value in it. Please help in this. Thanks in advance.
My Login page
export default class LoginForm extends Component<{}> {
constructor(props){
super(props)
this.state={
isLoggedIn:false,
userEmail:'',
userPassword:'',
}
}
login = () =>{
this.state.validating = true;
const {userEmail,userPassword} = this.state;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(userEmail==""){
this.setState({email:'Please enter Email address'})
}
else if(reg.test(userEmail) === false)
{
this.setState({email:'Email is Not Correct'})
return false;
}
else if(userPassword==""){
this.setState({email:'Please enter password'})
}
else{
fetch('http://mojse.com/wetest/userlogin.php',{
method:'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
email: userEmail,
password: userPassword
})
})
.then((response) => response.json())
.then((responseJson)=>{
let data = responseJson.data;
if (this.saveToStorage(data)){
/* Redirect to home page */
Actions.profile()
} else {
alert("Wrong Login Details");
}
})
.catch((error)=>{
console.error(error);
});
}
Keyboard.dismiss();
}
render(){
return(
<View style={styles.container}>
<TextInput style={styles.inputBox}
underlineColorAndroid='rgba(0,0,0,0)'
placeholder="Email"
placeholderTextColor = "#ffffff"
selectionColor="#fff"
keyboardType="email-address"
onChangeText={userEmail => this.setState({userEmail})}
/>
<TextInput style={styles.inputBox}
underlineColorAndroid='rgba(0,0,0,0)'
placeholder="Password"
secureTextEntry={true}
placeholderTextColor = "#ffffff"
ref={(input) => this.password = input}
onChangeText={userPassword => this.setState({userPassword})}
/>
<TouchableOpacity style={styles.button} onPress={this.login} >
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
</View>
)
}
async saveToStorage(userData){
if (userData) {
await AsyncStorage.setItem('user', JSON.stringify({
isLoggedIn: true,
authToken: this.state.authToken,
id: this.state.userid,
name: "KKKKKK"
})
);
return true;
}
return false;
}
}
You can get user data in the componentDidMount and save it to a state like this:
constructor(props){
super(props)
this.state={
userEmail:'',
userPassword:'',
userName:'',
}
}
componentDidMount() {
AsyncStorage.getItem('user').then((uservalue)=>{
uservalue = JSON.Parse(uservalue)
this.setState({userName: uservalue.name})
})
}
Now, you can use userName like this:
<Text style={styles.name}>{this.state.userName}</Text>
EDIT
First, please check that server response is correct ( maybe console.log(data) before save). Second, you are calling an async function so you have to wait until save function finish its work. also in save function, double check your data. my suggestion:
fetch('http://mojse.com/wetest/userlogin.php',{
method:'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
email: userEmail,
password: userPassword
})
})
.then((response) => response.json())
.then(async (responseJson) => { // this is an async function
let data = responseJson.data;
console.log(data) // check and validate data correction
let res = await this.saveToStorage(data)
if (res){
/* Redirect to home page */
Actions.profile()
} else {
alert("Wrong Login Details");
}
})
.catch((error)=>{
console.error(error);
});
saveToStorage = async (userData) => {
if (userData) {
let model = { // full model with received data. this.state. authToken is not valid because we do not have a state called authToken.
isLoggedIn: true,
authToken: userData.authToken,
id: userData.userid,
name: userData.name
}
await AsyncStorage.setItem('user', JSON.stringify(model))
return true;
}
return false;
}
this is what i thik may be wrong and i did not test it. double check your code please.
I hope this can help you.

Only Android users getting this error on createuserwithemailandpassword

On iOS this has never been an issue, but a lot of my users are attempting to create a firebase user, then I write that newly created user's info in the realtime database. It's hit or miss, some users it works successfully, sometimes it takes more than one try. Let me add that I have only been on this project for a short time and I can already tell best practices are not being used. The Following is the code:
Using crashlytics, I am seeing the folllwing error:
Fatal Exception: com.facebook.react.common.JavascriptException
null is not an object (evaluating 't.navigator.dispatch'), stack: #364:2006 value#49:1280 #605:1154 value#49:1280 #590:497 value#49:1280 value#28:3311 #28:822 value#28:2565 value#28:794 value#-1
screens/login.js
import React, { Component } from 'react';
import { ... } from 'react-native';
import { connect } from 'react-redux';
import { authActions, ... } from '../redux/actions';
import firebase from 'react-native-firebase';
class Login extends Component {
static navigationOptions = () => ({
headerMode: 'none',
header: null,
});
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
forceCheckEmail: false,
forceCheckPassword: false,
};
}
componentWillReceiveProps(newProps) {
const { props } = this;
const {
error,
isBusy,
dismissError,
screenProps: {
modal: {
setContent,
clearContent,
setDismissHandler,
},
},
} = newProps;
if (props.error !== error || props.isBusy !== isBusy) {
const modalContent =
isBusy ? <Spinner text='One moment...' /> :
error ? <ErrorPopup message={error} /> :
null;
if (modalContent) {
setContent(modalContent, undefined, this.ref);
setDismissHandler(() => {
this.setState({ showForgotBlock: true })
dismissError();
});
} else {
clearContent();
}
}
}
handleLogin() {
Keyboard.dismiss();
this.props.login({
email: this.state.email,
password: this.state.password,
});
}
render() {
const {
keyboardIsVisible,
email,
password,
forceCheckEmail,
forceCheckPassword,
showForgotBlock,
} = this.state;
const {
...
navigation: {
navigate
}
} = this.props;
const emailValid = validateEmail(email);
const passwordValid = password.length > 5;
const loginEnabled = email !== '' && emailValid && passwordValid;
const forgotPasswordBlock = showForgotBlock ? (
<TouchableOpacity
onPress={() => restorePassword(email)}
style={{marginTop: -20, marginBottom: 10}}
>
<Text style={{color: '#777'}}>
Forgot your password?
</Text>
</TouchableOpacity>
): null;
firebase.analytics().setCurrentScreen('login', 'login');
return (
...
<TextInput
style={[styles.input, forceCheckEmail && !emailValid ? styles.failedInput : null]}
autoCorrect={false}
placeholder="Email"
onBlur={() => this.setState({ forceCheckEmail: true })}
autoCapitalize="none"
keyboardType="email-address"
placeholderTextColor={color.INPUT_TEXT}
onChangeText={email => this.setState({ email })}
value={email}
/>
<TextInput
style={[styles.input, forceCheckPassword && !passwordValid ? styles.failedInput : null]}
autoCorrect={false}
placeholder="Password"
onBlur={() => this.setState({ forceCheckPassword: true })}
placeholderTextColor={color.INPUT_TEXT}
secureTextEntry
onChangeText={password => this.setState({ password })}
value={password}
/>
...
<TouchableOpacity
style={[styles.button, styles.buttonPrimary]}
onPress={() => navigate('SignUp')}
>
<Text style={styles.buttonPrimaryText}>
SIGN UP
</Text>
</TouchableOpacity>
...
export default connect(
state => ({
...
}),
{
login: data => authActions.login(data),
...
},
)(Login);
actions/auth.js
import { createActions } from 'redux-feline-actions';// I question this dependency
import firebase from 'react-native-firebase';
import FBSDK from 'react-native-fbsdk';
const usersDB = firebase.database().ref('users');
const newUserData = {
point: 0,
savedNumbers: [],
};
export default createActions({
...
register: ({ name, email, phone, password }) => ({
useReducer: 'auth',
payload: firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then(({user: { uid, email }}) => usersDB
.child(uid)
.set({
...newUserData,
name,
email,
phone,
id: uid,
})
.then(err => err || ({
...newUserData,
name,
email,
phone,
id: uid,
}))),
}),
...
stores/auth.js
import Immutable, { Map } from 'immutable';
import createAsyncStores from 'cat-stores'; // I also question this one
export default createAsyncStores({
auth: {
begin: state => state
.set('isBusy', true),
complete: (state, { payload }) => state
.set('isBusy', false)
.set('user', Immutable.fromJS(payload)),
error: {
default: (state, { payload }) => state
.set('error', payload.message)
.set('isBusy', false)
.set('user', null), // Android users keep getting this result I believe
},
},
...
},
Map({
isBusy: false,
error: null,
user: null,
redirectTo: null,
theme: Map(),
settings: Map(),
themeIsLoaded: false,
settingsAreLoaded: false,
}));
I expect the user to not have an issue with creating and saving new user info on Android, just like on iOS.

React Native Form Validation

I created a login form using react-native and I want to validate every fields but I don't know how to do it. I'm quite new to react-native so I want to ask anyone for help. Form validation should show error under following conditions:
Input form is empty
Email text isn't email form.
Password text does not satisfy the conditions above.
If Input form has errors the login button should be disabled.
If Input form doesn't have any errors, show alert to inform login
success
Sample image validation:
Here is my code:
import React from 'react';
import { StyleSheet, Text, View, Image, TextInput, Dimensions, ScrollView,
CheckBox, TouchableOpacity } from 'react-native';
import logo from './image/Logo.png'
const { width: WIDTH } = Dimensions.get('window')
export default class App extends React.Component {
constructor(){
super();
this.state={
check:false,
email: '',
};
this.validates = this.validates.bind(this);
}
CheckBoxText(){
this.setState({
check:!this.state.check,
})
}
validates = () => {
let text = this.state.email;
let emailError = this.state.emails;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(text) === false)
{
console.warn("Invalid email")
this.setState({email:text})
return false;
}
else {
this.setState({email:text})
console.log("Email is Correct");
}
}
render() {
return (
<View>
<View style={styles.container}>
<Image source={logo} style={styles.logo}/>
</View>
<View style = {styles.container2}>
<Text style={styles.emailAdd}>
Email
</Text>
<TextInput
onChangeText={(text) => this.setState({email:text})}
type='email'
value={this.state.email}
keyboardType='email-address'
style={styles.emailInput}
placeholder={'Input Email Address'}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container3}>
<Text style={styles.password}>
Password
</Text>
<TextInput
style={styles.passwordInput}
placeholder={'Input Password'}
secureTextEntry={true}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container4}>
<View>
<CheckBox value={this.state.check} onChange={()=>this.CheckBoxText()} style={styles.rememberMe}/>
</View>
<View>
<Text style={styles.remember}>Remember me</Text>
</View>
</View>
<TouchableOpacity style={styles.btnLogin} onPress={this.validates} >
<Text style={styles.txtLogin}>Sign In</Text>
</TouchableOpacity>
</View>
);
}
}
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
if (!email) {
Toast.show('Email is required.');
} else if (!email.match(validRegex)) {
Toast.show('Invalid Email');
} else if (!password) {
Toast.show('Password is required.');
}
I recommend using formik and yup to easily build a login form with input validation. These two packages when integrated together, simplifies your codebase thanks to both of its features.
Please take a look at a CodeSandbox snippet here, https://codesandbox.io/s/stack-overflow-54204827-llvkzc?file=/index.tsx:254-656. And note, I'm using typescript here.
The package.json file at the time of written snippet is:
"dependencies": {
...
"formik": "2.2.9",
...
"yup": "0.32.11"
},
And to break the solution down, first we define our yup schema for our Login form:
Note, you may tweak the regex pattern later, as this password validation accepts min 6 to max 12 characters, with at least one uppercase letter, one lowercase letter, one number and one special character.
/**
* The `yup` Login Form schema
*/
const LoginSchemaA = Yup.object().shape({
email: Yup.string()
.email("Invalid email.")
.required("Email must be provided."),
password: Yup.string()
.required("Password must be provided.")
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{6,12})/,
"Password must be minimum 6 and maximum 12 characters."
)
});
Note, .email("Invalid email.") here is the default email validation feature used. You can remove this, and use .matches(...) function instead for your own regular expression.
And just the <Formik /> section for your further use:
<Formik
initialValues={{
email: "",
password: ""
}}
validationSchema={LoginSchemaA}
onSubmit={(
values: Values,
{ setSubmitting }: FormikHelpers<Values>
) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
>
{({ errors, touched }) => (
<Form>
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="john.doe#email.com"
type="email"
/>
{errors.email && touched.email ? (
<div style={{ color: "red" }}>{errors.email}</div>
) : null}
<label htmlFor="password">Password</label>
<Field id="password" name="password" type="password" />
{errors.password && touched.password ? (
<div style={{ color: "red" }}>{errors.password}</div>
) : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
Lastly, you would want to grab the values itself for further use - ignore the setTimeout, alert and setSubmitting usages.
An example output of JSON.stringify(values, null, 2) would be as below:
{
"email": "john.doe#test.com",
"password": "Awesome#Password!2022"
}
Hope this helps you in your react-native coding journey!
here is my code you can try this
import React, { Component } from "react"
import { View, Button } from "react-native"
import TextField from "textfield"
import validation from "validation"
import validate from "validation_wrapper"
export default class Form extends Component {
constructor(props) {
super(props)
this.state = {
email: "",
emailError: "",
password: "",
passwordError: ""
}
}
register() {
const emailError = validate("email", this.state.email)
const passwordError = validate("password", this.state.password)
this.setState({
emailError: emailError,
passwordError: passwordError
})
if (!emailError && !passwordError) {
alert("Details are valid!")
}
}
render() {
return (
<View>
<TextField
onChangeText={(value) => this.setState({ email: value.trim() })}
onBlur={() => {
this.setState({
emailError: validate("email", this.state.email)
})
}}
error={this.state.emailError}
/>
<TextField
onChangeText={(value) => this.setState({ password: value.trim() })}
onBlur={() => {
this.setState({
passwordError: validate("password", this.state.password)
})
}}
error={this.state.passwordError}
secureTextEntry={true}
/>
<Button title="Register" onPress={this.validateRegister} />
</View>
)
}
}
<!-- begin snippet: js hide: false console: true babel: false -->
const validation = {
email: {
presence: {
message: "^Please enter an email address"
},
email: {
message: "^Please enter a valid email address"
}
},
password: {
presence: {
message: "^Please enter a password"
},
length: {
minimum: 5,
message: "^Your password must be at least 5 characters"
}
}
}
export default validation
import validation from "validation.js"
export default function validate(fieldName, value) {
// Validate.js validates your values as an object
// e.g. var form = {email: 'email#example.com'}
// Line 8-9 creates an object based on the field name and field value
var formValues = {}
formValues[fieldName] = value
// Line 13-14 creates an temporary form with the validation fields
// e.g. var formFields = {
// email: {
// presence: {
// message: 'Email is blank'
// }
// }
var formFields = {}
formFields[fieldName] = validation[field]
// The formValues and validated against the formFields
// the variable result hold the error messages of the field
const result = validatejs(formValues, formFields)
// If there is an error message, return it!
if (result) {
// Return only the field error message if there are multiple
return result[field][0]
}
return null
}
import React from "react"
import { View, TextInput, Text } from "react-native"
const TextField = (props) => (
<View>
<TextInput />
props.error ? <Text>{props.error}</Text> : null
</View>
)
export default TextField

Why isn't mailchimp API working with fetch?

I'm trying to add an email address to a mailchimp list I have.
This is for a react native app and I'm trying to implement the request using fetch.
This is my code within the component:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import { emailChanged, nameChanged, addToWaitingList } from '../actions';
import { Card, CardSection, Input, Button, Spinner } from '../components/Auth';
class addToWaitingListForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
onButtonPress() {
const { email } = this.props;
this.props.addToWaitingList({ email });
}
renderButton() {
if (this.props.loading) {
return <Spinner size="large" />;
}
return (
<Button onPress={this.onButtonPress.bind(this)}>
Keep me in the loop!
</Button>
);
}
render() {
return (
<View>
<Card>
<CardSection>
<Input
placeholder="your name"
onChangeText={this.onNameChange.bind(this)}
value={this.props.name}
/>
</CardSection>
<CardSection>
<Input
placeholder="email#uni.ac.uk"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
</CardSection>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
<CardSection style={{ borderBottomWidth: 0 }}>
{this.renderButton()}
</CardSection>
</Card>
</View>
);
}
}
const mapStateToProps = ({ auth }) => {
const { email, name, error, loading } = auth;
return { email, name, error, loading };
};
export default connect(mapStateToProps, {
emailChanged,
addToWaitingList
})(addToWaitingListForm);
Add this is my action code for interacting with the mailchimp api:
import Router from '../../navigation/Router';
import { getNavigationContext } from '../../navigation/NavigationContext';
export const addToWaitingList = ({ email }) => {
const emailListID = 'e100c8fe03';
fetch(`https://us13.api.mailchimp.com/3.0/lists/${emailListID}/members/`, {
method: 'POST',
body: JSON.stringify({
'email_address': email,
'status': 'subscribed',
'merge_fields': {
'FNAME': 'Urist',
'LNAME': 'McVankab'
}
})
})
.then(() => addSubscriberSuccess())
.catch(error => console.log(error));
};
const addSubscriberSuccess = () => {
getNavigationContext().getNavigator('root').immediatelyResetStack([Router.getRoute('auth')]);
};
Right now, the error I'm just getting back is ExceptionsManager.js:62 Cannot read property 'type' of undefined and Error: unsupported BodyInit type
What does this mean and how can I fix this?
You need to do two things.
First off you need to send the basic authentication via fetch so you cant do "user:pass" You have to convert it with btoa('user:pass').
Then you have to send it with mode: 'no-cors'
let authenticationString = btoa('randomstring:ap-keyxxxxxxx-us9');
authenticationString = "Basic " + authenticationString;
fetch('https://us9.api.mailchimp.com/3.0/lists/111111/members', {
mode: 'no-cors',
method: 'POST',
headers: {
'authorization': authenticationString,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email_address: "dude#gmail.com",
status: "subscribed",
})
}).then(function(e){
console.log("fetch finished")
}).catch(function(e){
console.log("fetch error");
})