I'm trying to run a test on login form submit which goes through a Rest API call to authenticate a user.
I have configured MSW for mocking the rest API. Whenever I am running the npm test command the rest api call isn't going through and returning an error. Seems like the mock API isn't working in this case
I have configured MSW for mocking the rest API. I am attaching the handler file, jest setup file and screen file below for reference.
jest.setup.config
import mockAsyncStorage from '#react-native-async-storage/async-storage/jest/async-storage-mock';
import 'whatwg-fetch';
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
jest.mock('#react-native-async-storage/async-storage', () => mockAsyncStorage);
login.screen.tsx
import React from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { Button, Pressable, SafeAreaView, Text, View } from 'react-native';
import styles from './EmailLogin.style';
import Input from '../../components/Input/Input.component';
import { color } from '../../theme';
import LinearGradient from 'react-native-linear-gradient';
import { setUser } from '../../stores/user.reducer';
import { useLoginUserMutation } from '../../services/api/auth';
import { useAppDispatch } from '../../hooks/redux';
const EMAIL_PATTERN = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i;
interface IEmailFormInputs {
email: string;
password: string;
}
const EmailLogin: React.FC = ({ navigation }: any) => {
const dispatch = useAppDispatch();
const [loginUser, { isLoading, isError, error, isSuccess }] =
useLoginUserMutation();
const onSubmit: SubmitHandler<IEmailFormInputs> = async requestData => {
try {
const { result } = await loginUser(requestData).unwrap();
dispatch(setUser(result));
navigation.navigate('Home');
} catch (err) {
console.log(err);
}
};
const {
control,
handleSubmit,
formState: { errors },
} = useForm<IEmailFormInputs>({
mode: 'onChange',
defaultValues: {
email: '',
password: '',
},
});
return (
<SafeAreaView style={styles.whiteBackground}>
<View style={styles.mainContainer}>
<View style={styles.welcomeTextContainer}>
<Text style={styles.welcomeTextTitle}>HELLO,</Text>
<Text style={styles.welcomeTextSubTitle}>Welcome back</Text>
</View>
<View style={styles.inputContainer}>
<Input
name="email"
control={control}
rules={{
pattern: {
value: EMAIL_PATTERN,
message: 'Invalid email address',
},
}}
error={errors.email}
placeholder={'Email'}
autoCapitalize={'none'}
autoCorrect={false}
/>
</View>
<View style={styles.inputContainer}>
<Input
name="password"
control={control}
rules={{
minLength: {
value: 3,
message: 'Password should be minimum 3 characters long',
},
}}
secureTextEntry={true}
error={errors.password}
placeholder={'Password'}
autoCapitalize={'none'}
autoCorrect={false}
/>
</View>
<Pressable onPress={handleSubmit(onSubmit)}>
<LinearGradient
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
colors={color.gradient.primary}
style={styles.buttonContainer}>
<Text style={styles.buttonText}>Login</Text>
</LinearGradient>
</Pressable>
</View>
</SafeAreaView>
);
};
export default EmailLogin;
login.spec.js
import React from 'react';
import { render, screen, fireEvent, waitFor, act } from '../../test-utils';
import { server } from '../../mocks/server';
import EmailLogin from './EmailLogin.screen';
describe('Home', () => {
//handles valid input submission
it('handle valid form submission', async () => {
await render(<EmailLogin />);
await act(async () => {
fireEvent(
screen.getByPlaceholderText('Email'),
'onChangeText',
'sample#email.com',
);
fireEvent(
screen.getByPlaceholderText('Password'),
'onChangeText',
'password',
);
fireEvent.press(screen.getByText('Login'));
//expected result on success login to be mentioned here
});
});
});
mocks/handler.js
import { rest } from 'msw';
import config from '../config';
export const handlers = [
rest.post(`*url*/user/auth`, (req, res, ctx) => {
console.log(req);
// successful response
return res(
ctx.status(200),
ctx.json({
success: true,
result: {
auth_token: ['authtoken'],
email: req.bodyUsed.email,
id: 1,
first_name: 'xxx',
last_name: 'xxx',
number: 'xxxxxxx',
user_state: 1,
phone_verified: true,
},
}),
);
}),
];
I get the following error when
console.log
{ status: 'FETCH_ERROR', error: 'AbortError: Aborted' }
at src/screens/EmailLogin/EmailLogin.screen.tsx:32:15
at Generator.throw (<anonymous>)
Related
In my project I have many users and many resources (and many user_resources/the join between users and resources).When I POST to user_resources I see it work on my rails backend (as in I see that instance posted) but in my react native front end I don't see it listed upon update. However, once the app is completely refresh (when I stop and restart the expo server), I finally see those items rendered. ANY IDEAS? I've been working on this forever now to no avail and my project is due tmrw, so any help is appreciated.
screen where I post to user_resources:
import React from 'react';
import { ScrollView,SafeAreaView,StyleSheet, Text, View, FlatList, TouchableOpacity,Button, NativeEventEmitter} from 'react-native';
import {connect} from 'react-redux';
import {fetchResources,searchChanged} from '../actions';
import { addUserResource } from '../actions'
import {SearchBar} from 'react-native-elements';
import { MaterialIcons } from '#expo/vector-icons';
import { MaterialCommunityIcons } from '#expo/vector-icons';
class ResourcesScreen extends React.Component {
state = {
search: ''
}
componentDidMount = () =>{
this.props.fetchResources();
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "lightblue",
}}
/>
);
}
handlePress(item) {
debugger
fetch('http://localhost:3000/user_resources', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
resource_id: item.id,
user_id: this.props.users.id,
name: item.name,
description:item.description,
link:item.link,
})
})
.then(res => res.json())
.then(data2 => {
console.log(data2)
this.props.addUserResource(data2)
console.log(this.props)
})
}
header = () => {
return <View>
<Text style={styles.header}>Resources</Text>
</View>
}
onSearchChange = text => {
this.setState({search:text})
}
render(){
return(
<SafeAreaView>
<SearchBar placeholderTextColor="white" placeholder="Enter resource name here" onChangeText={this.onSearchChange} value={this.state.search}/>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Add A New Resource',{topicId:this.props.route.params.topicId})} style={styles.buttonitem}>
<Text style={styles.text}>
<MaterialIcons name="add-circle-outline" size={24} color="white"/>Add A New Resource
</Text>
</TouchableOpacity>
<FlatList keyExtractor={(item)=> item.id.toString()} data={this.props.resourceName} ItemSeparatorComponent = { this.FlatListItemSeparator } renderItem={({item}) => {
return <TouchableOpacity style={styles.material2}>
<Text onPress={() => this.props.navigation.navigate('Add A New Resource',{topicId:item.id})} style={styles.listitem}>{item.name}</Text>
<MaterialCommunityIcons name="bookmark-plus" size={50} color="#16a085" backgroundColor='black' onPress={()=>this.handlePress(item)}/>
</TouchableOpacity>
}}
ListHeaderComponent = {this.header}/>
</SafeAreaView>
)
}
}
const mapStateToProps = (state) => {
return {
resourceName: state.resourceReducer.resources,
users: state.userReducer,
search:state.resourceReducer.searchTerm
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchResources: () => dispatch(fetchResources()),
addUserResource,
searchChanged
}
}
export default connect(mapStateToProps,mapDispatchToProps)(ResourcesScreen)
After this I head to the profile page where the user_resources SHOULD be displayed, but aren't
import React from 'react';
import { ScrollView,StyleSheet, Text, View, FlatList, TouchableOpacity} from 'react-native';
import {connect} from 'react-redux';
import {SearchBar} from 'react-native-elements';
import { AntDesign } from '#expo/vector-icons';
class Profile extends React.Component{
handleDelete = (id) => {
debugger
fetch(`http://localhost:3000/user_resources/${id}`, {
method: "DELETE",
headers: {
"Authorization": this.props.users.token
}
})
.then(r => r.json())
.then((delResource) => {
console.log(delResource)
this.props.deleteOneFood(delResource)
console.log('deleted')
this.forceUpdate()
})
}
render(){
return(
<View>
{this.props.users.user_resources.map(singleResource=> {
return <Text key={singleResource.id}>{singleResource.name}</Text>
})}
</View>
)}
}
let deleteOneResource = (id) => {
return {
type: "DELETE_ONE_USER_RESOURCE",
payload: id
}
}
const mapDispatchToProps = {
deleteOneResource
}
const mapStateToProps = (state) => {
return {
users: state.userReducer,
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Profile)
I had a flatlist before but thought that may be causing the issues so rendered it another way, still no luck. I tried forceUpdates as well, still no luck. I'm not sure if the issue is coming from my reducer:
let userInitialState = {
id: 0,
username: "",
name: '',
category: '',
token: "",
user_resources:[],
}
let userReducer = (state = userInitialState, action) => {
switch(action.type){
case "ADD_USERS":
let singleNestedObject = {
...action.users.user,
token: action.users.token
}
return {
...state,
username: action.users.user.username,
token: action.users.token,
id: action.users.user.id,
name: action.users.user.name,
category: action.users.user.category,
user_resources: action.users.user.user_resources
}
case "ADD_ONE_USER_RESOURCE":
let copyOfResources = [...state.user_resources, action.userResources]
return {
...state,
user_resources: copyOfResources
}
default:
return state
}
}
and it's action
export const addUserResource = (resourceInfo) => {
return {
type: "ADD_ONE_USER_RESOURCE",
userResources: resourceInfo
}
}
Please help me find the issue here, I'm losing it.
I have a login form, and when the user/password are not correct I call a function to make a animation in a component, but this function is only called when I pass the user/password to my api and it does not match with the information in the database. I want to call the function when the form is not valid too (before go to my api).
I'm using the formik lib to validate my form, and with that I show the message error, but I don't know how to make it call my animation function. My code is below:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { login } from '../../store/actions/user'
import {
View,
Text,
Animated,
Easing
} from 'react-native'
import { Formik } from 'formik'
import AuthContainer from '../../components/Auth/AuthContainer'
import AuthTitle from '../../components/Auth/AuthTitle'
import AuthButton from '../../components/Auth/AuthButton'
import AuthInput from '../../components/Auth/AuthInput'
import AuthHeader from '../../components/Auth/AuthHeader'
import SpeechBubble from '../../components/Auth/SpeechBubble'
import { validationSchema } from './validationSchema'
import styles from '../../assets/css/styles'
class Login extends Component {
state = {
email: '',
password: '',
shakeAnimation: new Animated.Value(0)
}
componentDidUpdate = prevProps => {
if(prevProps.isLoading && !this.props.isLoading && this.props.token){
this.props.navigation.navigate('Main')
}
else if(this.props.loginError && this.props.isLoading){
this.handleAnimation()
}
}
login = () => {
this.props.onLogin({ ...this.state })
}
navigateToRegister = () => {
this.props.navigation.navigate('Register')
}
handleAnimation = () => {
Animated.sequence([
Animated.timing(this.state.shakeAnimation, { toValue: 10, duration: 80, useNativeDriver: true }),
Animated.timing(this.state.shakeAnimation, { toValue: -10, duration: 80, useNativeDriver: true }),
Animated.timing(this.state.shakeAnimation, { toValue: 10, duration: 80, useNativeDriver: true }),
Animated.timing(this.state.shakeAnimation, { toValue: 0, duration: 80, useNativeDriver: true })
]).start()
}
render(){
return (
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values) => {
this.setState({ email: values.email, password: values.password })
this.login()
}}
validationSchema={validationSchema}
validateOnBlur={false}
validateOnChange={false} >
{formikProps => (
<AuthContainer>
<AuthHeader />
<SpeechBubble>
<Animated.View
style={{ transform: [{translateX: this.state.shakeAnimation}] }}>
<AuthTitle
text={
formikProps.errors.email ?
formikProps.errors.email :
formikProps.errors.password ?
formikProps.errors.password :
!this.props.loginError ?
'Olá! Cadastrou sua conta usando e-mail e senha? Insira-os aqui:' :
this.props.loginError}
styleTitle={!this.props.loginError && formikProps.isValid ? styles.bodyText :
[styles.bodyText, styles.bodyTextError]}/>
</Animated.View>
<AuthInput
placeholder='Seu e-mail'
keyboardType='email-address'
autoFocus={true}
autoCorrect={false}
autoCapitalize='none'
returnKeyType='next'
value={formikProps.values.email}
onChangeText={formikProps.handleChange('email')}
onBlur={formikProps.handleBlur('email')}
blurOnSubmit={false} />
<AuthInput
placeholder='Sua senha'
secureTextEntry={true}
returnKeyType='go'
value={formikProps.values.password}
onBlur={formikProps.handleBlur('password')}
onChangeText={formikProps.handleChange('password')} />
<AuthButton title='Entrar' onPress={formikProps.handleSubmit} />
<View style={styles.bodyBottom}>
<Text style={styles.bodyLink}>Esqueci minha senha</Text>
<Text style={styles.bodyLink} onPress={this.navigateToRegister}>
Ainda não cadastrei uma conta
</Text>
</View>
</SpeechBubble>
</AuthContainer>
)}
</Formik>
)
}
}
const mapStateToProps = ({ user }) => {
return {
isLoading: user.isLoading,
token: user.token,
loginError: user.loginError,
}
}
const mapDispatchToProps = dispatch => {
return {
onLogin: user => dispatch(login(user))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
The place where you pass formikProps.handleSubmit, just pass a function that calls you api and after calls formikProps.handleSubmit
<AuthButton
title='Entrar'
onPress={() => {
// call what you want
// call formik handleSubmit after
formikProps.handleSubmit()
}}
/>
I understand that this.props.isValidUser gets updated after action dispatches the axios promise. if the user is not valid is shows message. If the user is valid user, I want to navigate to another screen to enter pin. How do I navigate to another screen after I get axios result from action?
types.js
export const VALIDATE_USER = "VALIDATE_USER";
export const VALIDATE_PIN = "VALIDATE_PIN";
export const GET_ERRORS = "GET_ERRORS";
Reducer.js
import { VALIDATE_USER, VALIDATE_PIN, GET_ERRORS } from "../actions/types.js";
export default function (state = initialState, action) {
switch (action.type) {
case VALIDATE_USER:
return {
...state,
isValidUser: (action.payload == true) ? true : false,
Id: action.employeeId
};
case VALIDATE_PIN:
return {
...state,
isValidPin: action.payload,
action: "VALIDATE_PIN",
};
default:
return state;
}
}
action.js
import { GET_ERRORS, VALIDATE_USER, VALIDATE_PIN, } from "./types";
export const validateUser = (empId) => dispatch => {
axios.get(`${API}/api/Account/ValidateMobileAppUser?employeeId=${empId}`)
.then(res => {
dispatch({
type: VALIDATE_USER,
payload: res.data,
Id: empId,
});
})
.catch(err => {
dispatch({
type: VALIDATE_USER,
payload: false,
Id: empId
});
});
};
Login.js
import PropTypes from "prop-types";
import { validateUser } from "../actions/authActions";
class Login extends PureComponent {
constructor() {
super();
this.state = {
employeeId: "",
pin: "",
isValidUser: false,
};
this.onValidateUser = this.onValidateUser.bind(this);
this.onEmployeeId = this.onEmployeeId.bind(this);
}
onEmployeeId(employeeId) {
this.setState({ employeeId });
}
onValidateUser() {
this.props.validateUser(this.state.employeeId);
}
render() {
const { loading } = this.props.loading;
return (
<KeyboardAvoidingView style={styles.login} >
<ScrollView showsVerticalScrollIndicator={false}>
<Block padding={[10, theme.sizes.base * 2]} onPress={Keyboard.dismiss}>
<Block middle>
<Input
placeholder={this.state.placeholder}
keyboardType={this.state.keyboardType}
style={[styles.input]}
value={this.state.employeeId}
onChangeText={this.onEmployeeId}
/>
{(this.props.isValidUser == false) ? (
<Text center style={{ color: "#C00000", marginTop: 15, fontSize: 14 }}>
Employee Id not registered. Please contact HR.
</Text>
) : ""}
<Button
gradient
style={styles.loginButton}
onPress={this.onValidateUser}
>
<Text white center>
Login
</Text>
</Button>
</Block>
<Button
onPress={() => this.onGoToStep(1)}
style={{
borderWidth: 1,
borderRadius: 30,
borderColor: "#E46932"
}}
>
<Text gray caption center style={{ color: "#E46932" }}>
Don't have an account? Sign Up
</Text>
</Button>
</Block>
</ScrollView>
</KeyboardAvoidingView>
);
}
}
Login.propTypes = {
validateUser: PropTypes.func.isRequired,
errors: PropTypes.object.isRequired
};
function reducerCallback(state, ownProps) {
if (state.auth.isValidUser == true) {
ownProps.navigation.navigate("mPin", { Id: state.auth.employeeId, type: "LOGIN" });
}
}
const mapStateToProps = (state, ownProps) => ({
auth: reducerCallback(state, ownProps),
isValidUser: state.auth.isValidUser,
errors: state.errors
});
export default connect(
mapStateToProps,
{
validateUser,
}
)(Login);
this.props.isValidUser == false tells me if the user is valid or not. But if the user is valid I'm navigating to another screen using reducerCallback() function. I'm not aware if this is the correct way to do so. My question is how to I navigate to another screen after I get return result from async axios action and How to I set local state using setState when I get callback from axios dispatch. Please guide
Try to below code:
login.js:
onValidateUser() {
this.props.validateUser({
empId: this.state.employeeId,
onSuccess: () => {
//Navigate to other screen
},
onFailure: () => {
//Alert error message
},
});
}
Action.js:
export const validateUser = ({empId, onSuccess, onFailure}) => dispatch => {
axios
.get(
`${API}/api/Account/ValidateMobileAppUser?employeeId=${empId}`
)
.then(res => {
dispatch({
type: VALIDATE_USER,
payload: res.data,
Id: empId,
});
onSuccess();
})
.catch(err => {
dispatch({
type: VALIDATE_USER,
payload: false,
Id: empId
});
onFailure()
});
};
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.
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");
})