Only Android users getting this error on createuserwithemailandpassword - react-native

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.

Related

RTK Query Error: 'AbortError: Aborted' while running test

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>)

How to navigate to another screen after axios action dispatch in reducer in react- native

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()
});
};

Stop MapStateToProps executing on screen load

I have created a register page and I am trying hook up a loading ticker while I create the user account.
I am running into an issue where mapStateToProps() is being executed whenever the screen loads meaning that any values I have being mapped error as the state is undefined. None of my reducers or actions are executing to cause mapStateToProps() to run. Have I set something within my screen to cause this to execute, I completely understand that my state is indeed undefined but why does mapStateToProps even run in the initial load?
.......
interface State {
name: string,
email: string;
mobileNo: string;
password: string;
passwordConf: string;
nameTouched: boolean;
emailTouched: boolean;
mobileNoTouched: boolean;
passwordTouched: boolean;
passwordConfTouched: boolean;
loading: boolean;
}
class RegisterScreen extends React.Component<{
navigation: any;
register: Function}, State> {
emailInputRef = React.createRef<FormTextInput>();
mobileNoInputRef = React.createRef<FormTextInput>();
passwordInputRef = React.createRef<FormTextInput>();
passwordConfInputRef = React.createRef<FormTextInput>();
readonly state: State = {
name: "",
email: "",
password: "",
mobileNo:"",
passwordConf: "",
emailTouched: false,
passwordTouched: false,
nameTouched: false,
mobileNoTouched: false,
passwordConfTouched: false,
loading: false
};
handleNameChange = (name: string) => {
this.setState({ name: name });
};
handleEmailChange = (email: string) => {
this.setState({ email: email });
};
handleMobileNoChange = (mobileNo: string) => {
this.setState({ mobileNo: mobileNo });
};
handlePasswordChange = (password: string) => {
this.setState({ password: password });
};
handlePasswordConfChange = (passwordConf: string) => {
this.setState({ passwordConf: passwordConf });
};
handleNameSubmitPress = () => {
if (this.emailInputRef.current) {
this.emailInputRef.current.focus();
}
};
handleEmailSubmitPress = () => {
if (this.mobileNoInputRef.current) {
this.mobileNoInputRef.current.focus();
}
};
handleMobileNoSubmitPress = () => {
if (this.passwordInputRef.current) {
this.passwordInputRef.current.focus();
}
};
handlePasswordSubmitPress = () => {
if (this.passwordConfInputRef.current) {
this.passwordConfInputRef.current.focus();
}
};
handleNameBlur = () => {
this.setState({ nameTouched: true });
};
handleEmailBlur = () => {
this.setState({ emailTouched: true });
};
handleMobileNoBlur = () => {
this.setState({ mobileNoTouched: true });
};
handlePasswordBlur = () => {
this.setState({ passwordTouched: true });
};
handlePasswordConfBlur = () => {
this.setState({ passwordConfTouched: true });
};
render() {
const {
name,
email,
password,
mobileNo,
passwordConf,
emailTouched,
passwordTouched,
nameTouched,
mobileNoTouched,
passwordConfTouched,
} = this.state;
const nameError =
!name && nameTouched
? strings.NAME_REQUIRED
: undefined;
const emailError =
!email && emailTouched
? strings.EMAIL_REQUIRED
: undefined;
const mobileError =
!mobileNo && mobileNoTouched
? strings.MOBILE_REQUIRED
: undefined;
const passwordError =
!password && passwordTouched
? strings.PASSWORD_REQUIRED
: undefined;
const passwordConfError =
!passwordConf && passwordConfTouched && (password === passwordConf)
? strings.PASSWORD_CONF_REQUIRED
: undefined;
return (
<KeyboardAvoidingView
style={styles.container}
behavior="padding"
>
<Image source={imagePath} style={styles.logo} />
<View style={styles.form}>
{/* Name */}
<FormTextInput
keyboardType={"default"}
value={this.state.name}
onChangeText={this.handleNameChange}
onSubmitEditing={this.handleNameSubmitPress}
placeholder={strings.NAME_PLACEHOLDER}
autoCorrect={false}
returnKeyType="next"
onBlur={this.handleNameBlur}
error={nameError}
/>
{/* Email */}
<FormTextInput
keyboardType={"email-address"}
ref={this.emailInputRef}
value={this.state.email}
onChangeText={this.handleEmailChange}
onSubmitEditing={this.handleEmailSubmitPress}
placeholder={strings.EMAIL_PLACEHOLDER}
autoCorrect={false}
returnKeyType="next"
onBlur={this.handleEmailBlur}
error={emailError}
/>
{/* MobileNo */}
<FormTextInput
keyboardType={"numeric"}
ref={this.mobileNoInputRef}
value={this.state.mobileNo}
onChangeText={this.handleMobileNoChange}
onSubmitEditing={this.handleMobileNoSubmitPress}
placeholder={strings.MOBILE_PLACEHOLDER}
autoCorrect={false}
returnKeyType="next"
onBlur={this.handleMobileNoBlur}
error={mobileError}
/>
{/* Password */}
<FormTextInput
keyboardType={"default"}
ref={this.passwordInputRef}
value={this.state.password}
onChangeText={this.handlePasswordChange}
onSubmitEditing={this.handlePasswordSubmitPress}
placeholder={strings.PASSWORD_PLACEHOLDER}
secureTextEntry={true}
returnKeyType="done"
onBlur={this.handlePasswordBlur}
error={passwordError}
/>
{/* Password Conf */}
<FormTextInput
keyboardType={"default"}
ref={this.passwordConfInputRef}
value={this.state.passwordConf}
onChangeText={this.handlePasswordConfChange}
placeholder={strings.PASSWORD_CONF_PLACEHOLDER}
secureTextEntry={true}
returnKeyType="done"
onBlur={this.handlePasswordConfBlur}
error={passwordConfError}
/>
<ActivityIndicator animating={true} />
<Button
title="Register"
onPress={() => this.props.register(
name,
email,
mobileNo,
password)}
disabled={!email || !password || !name || !password || !passwordConf}
/>
</View>
</KeyboardAvoidingView>
);
}
}
const mapStateToProps = (state) => {
console.log(this.state);
//On screen load this executes with state = undefined, not sure what's causing it to fire
return {
loading : state.creatingUser
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({register: register}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(RegisterScreen);
React Redux tries to improve performance by doing shallow equality reference checks on incoming props in shouldComponentUpdate but you need to remember that
shouldComponentUpdate method is not called for the initial render therfore mapStateToProps will run in the initial load

Why is it that when I switch between two tabs with lists, I get the error 'undefined is not an object'?

Thank you in advance for your help - I'm very new to app development.
My React Native app has a tab navigator with three tabs one for a feed, one for a list of events, and one for a list of users. When I switch from my feed tab - which renders a list of posts - back to my users tab and click on list item to view a user's profile, I get the following error:Error when clicking on list item
I suspect that this problem has something to do with how I've created the lists.
For my feed tab, this is how I define my list of posts:
renderFeed = () => {
if (this.props.loadingList) {
return <Spinner />;
} else if (this.props.error) {
return (<Text>{this.props.error}</Text>);
} return (
<List
enableEmptySections
dataArray={this.props.feedData}
renderRow={this.renderPost}
/>
);
}
renderPost = (post) => {
const { name, postContent, time } = post;
return (
<Card style={{ flex: 0 }}>
<CardItem>
<Left>
<Thumbnail source={{ uri: 'https://cdn.images.express.co.uk/img/dynamic/4/590x/LeBron-James-has-until-June-29-to-opt-out-of-his-contract-with-the-Cavaliers-978390.jpg?r=1529715616214' }} />
<Body>
<Text>{name}</Text>
<Text note>{time}</Text>
</Body>
</Left>
</CardItem>
<CardItem>
<Body>
<Text>{postContent}</Text>
</Body>
</CardItem>
</Card>
);
}
For my users tab, this is how I define my list of users:
renderActivesList = () => {
if (this.props.loadingList) {
return <Spinner />;
} else if (this.props.error) {
return (<Text>{this.props.error}</Text>);
} return (
<List
enableEmptySections
dataArray={this.props.listData}
renderRow={this.renderRow}
/>
);
}
renderRow = (active) => {
const name = `${active.firstName} ${active.lastName}`;
return (
<ListItem
key={name}
button
onPress={() => { this.onActiveSelect(name, active.rank); }}
>
<Body>
<Text>{name}</Text>
<Text note>{active.position}</Text>
</Body>
<Right>
<Text note>{active.rank}</Text>
</Right>
</ListItem>
);
}
I feel as if there must be some conflict going on here, as the error only occurs when clicking on a user from the user list, and only AFTER I switch to the feed tab (and thus render it).
Please let me know your thoughts. Thanks!
UPDATE 1:
I tried using the list prop 'keyExtractor' to generate a key for each list item. The same error occured however. If it's important: the 'List' component I use here is from the Native-Base library.
UPDATE 2:
In response to a comment, here is some additional information on how I am handling state using redux.
For my feed tab (list of posts), the actions file is:
import firebase from 'firebase';
import _ from 'lodash';
import {
POST_CHANGED,
SEND_BUTTON_PRESSED,
POST_SUCCESS,
REQUEST_FEED_DATA,
REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';
export const postChanged = (text) => {
return {
type: POST_CHANGED,
payload: text
};
};
export const sendButtonPressed = (postContent, firstName, lastName, rank, organization) => {
if (postContent) {
return (dispatch) => {
dispatch({ type: SEND_BUTTON_PRESSED });
const name = `${firstName} ${lastName}`;
const time = new Date().toLocaleString();
const comments = 0;
firebase.database().ref(`${organization}/posts`)
.push({ name, rank, time, comments, postContent })
.then(dispatch({ type: POST_SUCCESS }));
};
} return { type: '' };
};
export const fetchFeed = (organization) => {
return (dispatch) => {
dispatch({ type: REQUEST_FEED_DATA });
firebase.database().ref(`${organization}/posts`)
.on('value', snapshot => {
const array = _.map(snapshot.val(), (val) => {
return { ...val };
});
const feed = array.reverse();
dispatch({ type: REQUEST_FEED_DATA_SUCCESS, payload: feed });
});
};
};
And the corresponding reducer file is:
import {
POST_CHANGED,
SEND_BUTTON_PRESSED,
POST_SUCCESS,
REQUEST_FEED_DATA,
REQUEST_FEED_DATA_SUCCESS
} from '../constants/Types';
const INITIAL_STATE = {
postContent: '',
posting: false,
loadingList: true,
feedData: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case POST_CHANGED:
return { ...state, postContent: action.payload };
case SEND_BUTTON_PRESSED:
return { ...state, posting: true };
case POST_SUCCESS:
return { ...state, posting: false, postContent: '' };
case REQUEST_FEED_DATA:
return { ...state, loadingList: true };
case REQUEST_FEED_DATA_SUCCESS:
return { ...state, feedData: action.payload, loadingList: false };
default:
return { state };
}
};
For my users tab (list of users), the actions file is:
import firebase from 'firebase';
import _ from 'lodash';
import {
REQUEST_LIST_DATA,
REQUEST_LIST_DATA_SUCCESS,
REQUEST_LIST_DATA_FAILED,
FETCH_SELECTED_PROFILE,
FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';
export const fetchActivesList = (organization) => {
return (dispatch) => {
dispatch({ type: REQUEST_LIST_DATA });
firebase.database().ref(`${organization}/activesList`)
.on('value', snapshot => {
const activesList = _.map(snapshot.val(), (val, rank) => {
return { ...val, rank };
});
dispatch({ type: REQUEST_LIST_DATA_SUCCESS, payload: activesList });
});
};
};
export const fetchSelectedProfile = (organization, rank) => {
return (dispatch) => {
dispatch({ type: FETCH_SELECTED_PROFILE });
firebase.database().ref(`${organization}/profiles/${rank}`)
.on('value', snapshot => {
dispatch({ type: FETCH_SELECTED_PROFILE_SUCCESS, payload: snapshot.val() });
});
};
};
And the corresponding reducer file is:
import {
REQUEST_LIST_DATA,
REQUEST_LIST_DATA_SUCCESS,
REQUEST_LIST_DATA_FAILED,
FETCH_SELECTED_PROFILE,
FETCH_SELECTED_PROFILE_SUCCESS
} from '../constants/Types';
const INITIAL_STATE = {
loadingList: false,
loadingProfile: false,
error: '',
listData: [],
//selectedProfileStats
selectedAdmin: false,
selectedBrotherhoods: 0,
selectedChapters: 0,
selectedCommunityService: 0,
selectedDues: 0,
selectedFirstName: '',
selectedLastName: '',
selectedMixers: 0,
selectedPosition: '',
selectedOrganization: '',
selectedRank: '',
selectedGoodStanding: true,
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case REQUEST_LIST_DATA:
return { ...state, loadingList: true };
case REQUEST_LIST_DATA_SUCCESS:
return { ...state, listData: action.payload, loadingList: false, error: '' };
case REQUEST_LIST_DATA_FAILED:
return { ...state, error: action.payload, loadingList: false };
case FETCH_SELECTED_PROFILE:
return { ...state, loadingProfile: true };
case FETCH_SELECTED_PROFILE_SUCCESS:
return {
...state,
loadingProfile: false,
selectedAdmin: action.payload.admin,
selectedBrotherhoods: action.payload.brotherhoods,
selectedChapters: action.payload.chapters,
selectedCommunityService: action.payload.communityService,
selectedDues: action.payload.dues,
selectedFirstName: action.payload.firstName,
selectedLastName: action.payload.lastName,
selectedMixers: action.payload.mixers,
selectedPosition: action.payload.position,
selectedGoodStanding: action.payload.goodStanding,
selectedRank: action.payload.rank
};
default:
return state;
}
};
I am handling navigation using the 'react-navigation' library. This code is spread over two files, one is a switch navigator called 'AppNavigator.js' and looks like this:
import { createSwitchNavigator, createStackNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
import LoginScreen from '../screens/auth/LoginScreen';
import RegisterChapterScreen from '../screens/auth/RegisterChapterScreen';
import JoinChapterScreen from '../screens/auth/JoinChapterScreen';
const AuthStack = createStackNavigator(
{
Login: LoginScreen,
RegChapter: RegisterChapterScreen,
joinChapter: JoinChapterScreen
},
{
initialRouteName: 'Login'
}
);
export default createSwitchNavigator(
{
// You could add another route here for authentication.
// Read more at https://reactnavigation.org/docs/en/auth-flow.html
Auth: AuthStack,
Main: MainTabNavigator
},
{
initialRouteName: 'Auth'
}
);
The second file is a tab navigator called 'MainTabNavigator' and looks like this:
import React from 'react';
import { Platform } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import FeedScreen from '../screens/feedTab/FeedScreen';
import EventsScreen from '../screens/eventsTab/EventsScreen';
import CreateEventScreen from '../screens/eventsTab/CreateEventScreen';
import ActivesScreen from '../screens/activesTab/ActivesScreen';
import ProfileScreen from '../screens/activesTab/ProfileScreen';
//Feed Tab Navigation Setup
const FeedStack = createStackNavigator({
Feed: FeedScreen,
});
FeedStack.navigationOptions = {
tabBarLabel: 'Feed',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-paper${focused ? '' : '-outline'}` : 'md-paper'}
color={tintColor}
/>
),
};
//Events Tab Navigation Setup
const EventsStack = createStackNavigator({
EventsList: EventsScreen,
CreateEvent: CreateEventScreen
});
EventsStack.navigationOptions = {
tabBarLabel: 'Events',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-person${focused ? '' : '-outline'}` : 'md-person'}
color={tintColor}
/>
),
};
//Actives Tab Navigation Setup
const ActivesStack = createStackNavigator({
Actives: ActivesScreen,
SelectedProfile: ProfileScreen,
});
ActivesStack.navigationOptions = {
tabBarLabel: 'Actives',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-contacts${focused ? '' : '-outline'}` : 'md-contacts'}
color={tintColor}
/>
),
};
export default createBottomTabNavigator(
{
ActivesStack,
FeedStack,
EventsStack,
},
{
tabBarOptions: {
activeTintColor: 'red',
inactiveTintColor: 'gray',
}
}
);
Hopefully this is enough information, but please comment if you need to see other parts of my code.
Thank you
I've found the answer! I'm not entirely sure why, but it seems that the List needed a key. so I added a random key property to the List component by using the math.random() function and it fixed the error.

Passing Props from Stack Navigator to Drawer Navigator in React - Native

I want to navigate to a screen which is given in Stack Navigator from Drawer Navigation on the basis of a response from the API. Also, I need help with the passing of props from Drawer to Stack Navigator and vice versa.
Below is my code
** App.js **
import {
DrawerNavigator,
StackNavigator
} from "react-navigation";
import LoginPage from "./Components/LoginPage";
import RegisterPage from "./Components/RegisterPage";
import Dashboard from "./Components/Dashboard";
const DrawerNavigation = DrawerNavigator({
Login: {
screen: LoginPage
},
Register: {
screen: RegisterPage
}
});
const StackNavigation = StackNavigator({
Drawer: {
screen: DrawerNavigation
},
Dashboard: {
screen: Dashboard
}
}, {
headerMode: "none"
});
But when I print the props I get blank object
//import liraries
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
KeyboardAvoidingView
} from "react-native";
var validator = require("validator");
import { Constants } from "../_helpers/Constants";
import { loginFormStyles as styles } from "../_helpers/styles";
import UserServices from "../Services/userService";
// create a component
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
isEmailValid: false,
isPasswordValid: false,
emailErrorMessage: "",
passwordErrorMessage: "",
email: "",
password: "",
username: "",
userID: "",
loginMessages: ""
};
}
validateEmail = email => {
if (validator.isEmail(email) && email !== "") {
this.setState(
{
isEmailValid: true,
emailErrorMessage: "",
email: email
},
function() {}
);
} else {
this.setState(
{
isEmailValid: false,
emailErrorMessage: Constants.ERRORS.USER_NAME
},
function() {}
);
}
};
validatePassword = password => {
var passwordRegex = new RegExp(
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##$%^&*])(?=.{8,})"
);
if (
passwordRegex.test(password) &&
password !== "" &&
password.length >= 8
) {
this.setState(
{ isPasswordValid: true, errorMessagePassword: "", password: password },
function() {}
);
} else {
this.setState(
{
isPasswordValid: false,
passwordErrorMessage: Constants.ERRORS.PASSWORD
},
function() {}
);
}
};
onLogin = () => {
let userServicesLogin = new UserServices();
let email = this.state.email;
let password = this.state.password;
let body = { email: email, password: password };
let header = {
"Content-Type": Constants.HEADERS.APPLICATION_JSON
};
userServicesLogin
.userLogin(body, header)
.then(responseJson => {
if (responseJson.status === "SUCCESS") {
this.setState({
username: responseJson.user.username,
userID: responseJson.user._id,
loginMessages: responseJson.message
});
} else if (responseJson.status === "ERROR") {
this.setState({
username: "",
userID: "",
loginMessages: responseJson.message
});
}
console.log(this.props)
})
.catch(error => {
console.log("error",error);
});
};
render() {
let validEmail = this.state.isEmailValid;
let validPassword = this.state.isPasswordValid;
let loginErrorMessage = this.state.loginMessages;
let errorMessageEmail = this.state.emailErrorMessage;
let errorMessagePassword = this.state.passwordErrorMessage;
return (
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<Text style={styles.loginStatusMessage}>
{loginErrorMessage === "" ? "" : loginErrorMessage}
</Text>
<TextInput
style={styles.input}
placeholder="Enter your Username"
placeholderTextColor="#2C3A47"
returnKeyType="next"
onSubmitEditing={() => this.passwordInput.focus()}
autoCorrect={false}
keyboardType="email-address"
autoCapitalize="none"
onChangeText={email => this.validateEmail(email)}
/>
<Text style={styles.errorMessageEmail}>
{validEmail ? "" : errorMessageEmail}
</Text>
<TextInput
style={styles.input}
placeholder="Enter your Password"
secureTextEntry
placeholderTextColor="#2C3A47"
returnKeyType="go"
ref={input => (this.passwordInput = input)}
autoCapitalize="none"
onChangeText={password => this.validatePassword(password)}
onSubmitEditing={this.onLogin}
/>
<Text style={styles.errorMessagePassword}>
{validPassword ? "" : errorMessagePassword}
</Text>
<TouchableOpacity
activeOpacity={0.7}
style={
validEmail && validPassword
? styles.buttonContainerEnabled
: styles.buttonContainerDisabled
}
disabled={!(validEmail && validPassword) ? true : false}
onPress={this.onLogin}
>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
//make this component available to the app
export default LoginForm;
How should I proceed with the problem.?