React Native - Cannot read property [PROPERTY] of undefined (ignite 2, reduxsauce) - react-native

I'm having trouble using redux in my react native app. I cannot call an action in my component. I get the following error:
This is my AuthRedux.js
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
const { Types, Creators } = createActions({
login: ['email', 'password'],
logout: null
})
export const AuthTypes = Types
export default Creators
export const INITIAL_STATE = Immutable({
isLoggedIn: false,
email: null,
password: null
})
export const userLogin = (state, {email, password}) => {
return Object.assign({}, state, {
isLoggedIn: true
});//state.merge({ isLoggedIn: true, email, password})
}
export const userLogout = (state) => {
return state.merge({ isLoggedIn: false, email: null, password: null })
}
export const reducer = createReducer(INITIAL_STATE, {
[Types.USER_LOGIN]: userLogin,
[Types.USER_LOGOUT]: userLogout
})
And this is my component LoginScreen.js
import React, { Component } from 'react'
import { ScrollView, Text, KeyboardAvoidingView, TextInput, TouchableOpacity, Button } from 'react-native'
import { connect } from 'react-redux'
import { AuthActions } from '../Redux/AuthRedux'
// Add Actions - replace 'Your' with whatever your reducer is called :)
// import YourActions from '../Redux/YourRedux'
// Styles
import styles from './Styles/LoginScreenStyle'
class LoginScreen extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
opacity: 1.0,
isLoggedIn: false
}
}
render () {
return (
<ScrollView style={styles.container}>
<KeyboardAvoidingView behavior='position'>
<Text>LoginScreen</Text>
<TextInput style={{width: 100, backgroundColor: 'red', height: 50, marginTop: 10}} onChangeText={(text) => this.setState({email : text})}/>
<TextInput style={{width: 100, backgroundColor: 'yellow', height: 50, marginTop: 10}} onChangeText={(text) => this.setState({password : text})}/>
<Button title='Hola' onPress={this.onLogin}/>
</KeyboardAvoidingView>
</ScrollView>
)
}
onLogin = () => {
console.log(this.state.email);
this.setState({opacity: 0.5})
this.props.userLogin(this.state.email, this.state.password);
}
handleOnPress = () => {
this.setState({opacity: 0.5})
}
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.auth.isLoggedIn
}
}
const mapDispatchToProps = (dispatch) => {
return {
userLogin: (email, password) => dispatch(AuthActions.login(email, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)
I'm trying to call userLogin function from the onPress button which is assigned in mapDispatchToProps. I also have my rootReducer configured like this:
const rootReducer = combineReducers({
nav: require('./NavigationRedux').reducer,
github: require('./GithubRedux').reducer,
search: require('./SearchRedux').reducer,
auth: require('./AuthRedux').reducer
})
And the store is also given to the Provider in App.js
class App extends Component {
render () {
return (
<Provider store={store}>
<RootContainer />
</Provider>
)
}
}
I don't know why login action is not detected.

Instead of import { AuthActions } from '../Redux/AuthRedux', do import AuthActions from '../Redux/AuthRedux', because you are doing export default on the actionCreators which are the ones that you want to import right now.
You can also do export const AuthActions = Creators where you are doing export default Creators, and you can keep your import statement the same way you have right now.

Related

React Native app crashing with no error when selecting a value from RNPickerSelect

I'm working in an older codebase using React-Native 0.63 and I'm trying to add RNPickerSelect to the login page of an app. The app uses Redux for State management, I've created Types, Reducers, Selectors, Actions, and an Initial State for the Redux. After Importing the RNPickerSelect and the Picker appears on the screen, and allows interaction but whenever a value is selected the app crashes and gives no error.
Here's the LoginScreen.js Page
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Text, StatusBar, View, ScrollView, Platform, Image } from 'react-native'
import Config from 'react-native-config'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { Colors } from '../Themes/'
import LoginActions, { LoginSelectors } from '../Redux/LoginRedux'
import RNPickerSelect from 'react-native-picker-select'
// Styles
import styles from './Styles/LoginScreenStyle'
import Logo from '../Components/Logo'
import LoginForm from '../Components/LoginForm'
import { withNavigation } from 'react-navigation'
import I18n from '../I18n/'
class LoginScreen extends Component {
static propTypes = {
userName: PropTypes.string,
password: PropTypes.string,
pickerValue: PropTypes.string,
updateUserName: PropTypes.func,
updatePassword: PropTypes.func,
updatePickerValue: PropTypes.func,
navigation: PropTypes.object,
doLogin: PropTypes.func,
siteUrl: PropTypes.string,
updateSiteUrl: PropTypes.func,
isLoggingIn: PropTypes.bool,
initialize: PropTypes.func
}
componentDidMount () {
const { userName, password, siteUrl, initialize } = this.props
if (!userName && !password && !siteUrl) {
initialize(Config.USER_NAME, Config.PASSWORD, Config.SITE_URL)
}
}
handlePressLogin = () => {
const { userName, password, siteUrl } = this.props
this.props.doLogin(userName, password, siteUrl)
}
render () {
const { userName, password, siteUrl, updateUserName, updatePassword, updateSiteUrl, isLoggingIn, updatePickerValue, pickerValue } = this.props
return (
<ScrollView contentContainerStyle={styles.mainScrollViewContainer}>
<View style={styles.contentArea}>
<View style={{backgroundColor: 'red'}}>
<StatusBar
barStyle={Platform.OS == 'ios' ? 'dark-content' : 'light-content'}
backgroundColor={Colors.primaryColor}
/>
</View>
<View style={styles.formViewContainer}>
<Logo size='small' />
<View style={styles.welcomeTextContainer}>
<Text style={styles.LogoText}>{I18n.t('loginPageWelcomeText')}</Text>
</View>
<RNPickerSelect
onValueChange={updatePickerValue}
items={[
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
]}
value={pickerValue}
/>
<LoginForm
userName={userName}
onUserNameChange={updateUserName}
password={password}
onPasswordChange={updatePassword}
siteUrl={siteUrl}
onSiteUrlChange={updateSiteUrl}
handlePressLogin={this.handlePressLogin}
isLogginIn={isLoggingIn}
/>
</View>
</View>
<View style={{marginBottom: 20}}><Image resizeMode='contain' style={{width: '100%'}} source={require('../Images/footer.jpeg')} /></View>
</ScrollView>
)
}
}
const mapStateToProps = (state) => {
return {
userName: LoginSelectors.getUserName(state),
password: LoginSelectors.getPassword(state),
pickerValue: LoginSelectors.getPickerValue(state),
siteUrl: LoginSelectors.getSiteUrl(state),
isLoggingIn: LoginSelectors.isLoggingIn(state),
}
}
const mapDispatchToProps = {
updateUserName: LoginActions.updateUserName,
updatePassword: LoginActions.updatePassword,
updateSiteUrl: LoginActions.updateSiteUrl,
updatePickerValue: LoginActions.updatePickerValue,
doLogin: LoginActions.loginRequest,
initialize: LoginActions.initialize
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withNavigation
)(LoginScreen)
And here's my redux js
import { createReducer, createActions } from 'reduxsauce'
import { StartupTypes } from './StartupRedux'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
initialize: ['userName', 'password', 'siteUrl'],
updateSiteUrl: ['siteUrl'],
updateUserName: ['userName'],
updatePassword: ['password'],
updatePickerValue: ['pickerValue'],
loginRequest: ['userName', 'password', 'siteUrl'],
loginSuccess: ['data'],
loginFailure: null,
logout: null
})
export const LoginTypes = Types
export default Creators
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
userName: '',
password: '',
siteUrl: '',
pickerValue: '',
accessToken: null,
displayName: '',
vendorId: null,
fetching: null,
payload: null,
error: null,
})
/* ------------- Selectors ------------- */
export const LoginSelectors = {
getData: state => state.data,
getSiteUrl: state => state.login.siteUrl,
getUserName: state => state.login.userName,
getPassword: state => state.login.password,
getPickerValue: state => state.login.pickerValue,
isLoggingIn: state => state.login.fetching,
getAccessToken: state => state.login.accessToken,
getDisplayName: state => state.login.displayName,
getVendorId: state => state.login.vendorId,
}
/* ------------- Reducers ------------- */
export const initialize = (state, { userName, password, siteUrl }) =>
state.merge({ userName, password, siteUrl })
export const request = (state, { data }) =>
state.merge({ fetching: true, data, payload: null })
export const success = (state, action) => {
const { data: { token: accessToken, store_name: displayName, store_id: vendorId } } = action
//console.log(vendorId)
return state.merge({ fetching: false, error: null, password: '', accessToken, displayName, vendorId })
}
export const updateUserName = (state, action) => {
const { userName } = action
return state.merge({ userName })
}
export const updatePassword = (state, action) => {
const { password } = action
return state.merge({ password })
}
export const updatePickerValue = (state, action) => {
const { pickerValue } = action
return state.merge({ pickerValue })
}
export const updateSiteUrl = (state, action) => {
const { siteUrl } = action
return state.merge({ siteUrl })
}
// Something went wrong somewhere.
export const failure = state =>
state.merge({ fetching: false, error: true, payload: null })
export const logout = state =>
INITIAL_STATE
export const onStartup = state =>
state.merge({ fetching: false })
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.LOGIN_REQUEST]: request,
[Types.LOGIN_SUCCESS]: success,
[Types.LOGIN_FAILURE]: failure,
[Types.UPDATE_USER_NAME]: updateUserName,
[Types.UPDATE_PASSWORD]: updatePassword,
[Types.UPDATE_SITE_URL]: updateSiteUrl,
[Types.UPDATE_PICKER_VALUE]: updatePickerValue,
[Types.LOGOUT]: logout,
[Types.INITIALIZE]: initialize,
[StartupTypes.STARTUP]: onStartup,
})
I've tried different pickers but RNPickerSelect seems to be the only one that even starts to display when I use it.
I've also tried putting it a level lower in the <LoginForm> component but for the sake of trying to make it easier on myself I've moved it up into LoginScreen instead.
I'm very new to React/React Native as well as Redux so I apologize if this is a bit of a noob question. Any help is greatly appreciated!

The component for route 'ActivityFeed' must be a React component

I have looked through various similar posts here on SO regarding similar issue, but none of the answers solved it for me.
This is the full error:
So in my src/navigation/feed/stack.js it's being defined like so:
import React from 'react';
import {StackNavigator} from 'react-navigation';
import ActivityFeed from 'activity-feed/session-user/screens/Main';
import HamburgerButton from 'navigation-components/HamburgerButton';
import HeaderTitle from 'navigation-components/HeaderTitle';
import ActionAlertIndicator from 'navigation-components/ActionAlertIndicator';
import * as navConfig from '../config';
import * as cache from 'utils/cache';
const stack = StackNavigator(
{
ActivityFeed: {
screen: ActivityFeed,
navigationOptions: ({navigation}) => ({
header: (
<HeaderTitle
headerLeft={() => (
<HamburgerButton
onPress={() => navigation.navigate('DrawerOpen')}
/>
)}
headerRight={() => (
<ActionAlertIndicator
onPress={() => {
cache.setRouteStarter('MainDrawer');
navigation.navigate('ActionAlertsStack');
}}
/>
)}
/>
),
}),
},
},
{
navigationOptions: {
...navConfig.defaultStackConfig,
},
}
);
export default stack;
The actual component or screen is defined like so inside of src/activity-feed/session-user/screens/Main.js:
import React, {PureComponent} from 'react';
import {
FlatList,
StyleSheet,
AppState,
Platform,
Dimensions,
View,
Alert,
} from 'react-native';
import PropTypes from 'prop-types';
import OneSignal from 'react-native-onesignal';
import {Loading, SwippableCard, BottomAlert} from 'common-components';
import EmptyState from 'activity-feed/session-user/components/EmptyState';
import EventFeedCard from 'events/components/EventFeedCard';
import SurveyBallotFeedCard from 'surveys-ballots/components/FeedCard';
import MicroSurvey from 'surveys-ballots/components/MicroSurvey';
import ActionAlertFeedCard from 'action-alerts/components/ActionAlertFeedCard';
import MissingAddressCard from 'action-alerts/components/MissingAddressCard';
import ArticleFeedCard from 'articles/components/ArticleFeedCard';
import GetInvolvedFeedCard from 'account-settings/components/GetInvolvedFeedCard';
import {connect} from 'react-redux';
import {
fetchFeed,
handleContentSwipe,
undoSwipeAction,
hideUndoAlert,
} from 'activity-feed/actions';
import {setSelectedEvent} from 'events/actions';
import {setSelectedSurvey} from 'surveys-ballots/actions';
import {setSelectedAlert, getCampaignDetails} from 'action-alerts/actions';
import * as cache from 'utils/cache';
import {setSelectedArticle} from 'articles/actions';
import {
handleUpdateTopics,
handleUpdateGetInvoved,
} from 'account-settings/preferencesActions';
import {scale} from 'react-native-size-matters';
import {emptyStateStyles} from 'theme';
const {height} = Dimensions.get('window');
export class ActivityFeed extends PureComponent {
static propTypes = {
displayAlert: PropTypes.bool,
feed: PropTypes.array,
fetchFeed: PropTypes.func,
getCampaignDetails: PropTypes.func,
handleContentSwipe: PropTypes.func,
handleUpdateGetInvoved: PropTypes.func,
handleUpdateTopics: PropTypes.func,
hideUndoAlert: PropTypes.func,
lastSwippedElement: PropTypes.object,
loading: PropTypes.bool,
navigation: PropTypes.object,
setSelectedAlert: PropTypes.func,
setSelectedArticle: PropTypes.func,
setSelectedEvent: PropTypes.func,
setSelectedSurvey: PropTypes.func.isRequired,
undoSwipeAction: PropTypes.func,
userEmailIsValidForVoterVoice: PropTypes.bool,
};
constructor(props) {
super(props);
this.prompted = false;
this.state = {
refreshing: false,
appState: AppState.currentState,
};
}
async componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
if (!this.props.loading) {
const doRefresh = await cache.shouldRefresh('feed');
if (this.props.feed.length === 0 || doRefresh) {
this.props.fetchFeed();
}
cache.incrementAppViews();
}
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = async appState => {
if (
this.state.appState.match(/inactive|background/) &&
appState === 'active'
) {
cache.incrementAppViews();
const doRefresh = await cache.shouldRefresh('feed');
if (doRefresh) {
this.props.fetchFeed();
}
}
this.setState({appState});
};
_keyExtractor = ({Entity}) =>
(Entity.Key || Entity.Id || Entity.CampaignId || Entity.Code).toString();
_gotoEvent = event => {
cache.setRouteStarter('MainDrawer');
this.props.setSelectedEvent(event);
const title = `${event.LegislatureType} Event`;
this.props.navigation.navigate('EventDetails', {title});
};
_gotoSurveyBallot = survey => {
cache.setRouteStarter('MainDrawer');
this.props.setSelectedSurvey(survey);
this.props.navigation.navigate('SurveyDetails');
};
_gotoArticle = article => {
cache.setRouteStarter('MainDrawer');
this.props.setSelectedArticle(article);
this.props.navigation.navigate('ArticleDetails');
};
_onAlertActionButtonPress = async item => {
cache.setRouteStarter('MainDrawer');
await this.props.setSelectedAlert(item.Entity);
this.props.getCampaignDetails();
if (this.props.userEmailIsValidForVoterVoice) {
this.props.navigation.navigate('Questionnaire');
} else {
this.props.navigation.navigate('UnconfirmedEmail');
}
};
_onSwipedOut = (swippedItem, index) => {
this.props.handleContentSwipe(this.props, {swippedItem, index});
};
_handleGetInvolved = (response, entity) => {
if (response !== entity.IsSelected) {
const isTopic = entity.Category !== 'GetInvolved';
const items = [
{
...entity,
IsSelected: response,
},
];
if (isTopic) {
this.props.handleUpdateTopics({topics: items});
} else {
this.props.handleUpdateGetInvoved({involved: items});
}
}
};
renderItem = ({item, index}) => {
const {Type, Entity} = item;
if (Type === 'EVENT') {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<EventFeedCard
style={styles.push}
mainActionButtonPress={() => this._gotoEvent(Entity)}
event={Entity}
/>
</SwippableCard>
);
}
if (['SURVEY_SURVEY', 'SURVEY_BALLOT'].includes(Type)) {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<SurveyBallotFeedCard
style={styles.push}
survey={Entity}
handleViewDetails={() => this._gotoSurveyBallot(Entity)}
/>
</SwippableCard>
);
}
if (Type === 'SURVEY_MICRO') {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<MicroSurvey style={styles.push} selectedSurvey={Entity} />
</SwippableCard>
);
}
if (Type === 'ALERT') {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<ActionAlertFeedCard
datePosted={Entity.StartDateUtc}
style={styles.push}
title={Entity.Headline}
content={Entity.Alert}
mainActionButtonPress={() => this._onAlertActionButtonPress(item)}
secondaryActionButtonPress={() => {
this.props.setSelectedAlert(Entity);
// eslint-disable-next-line
this.props.navigation.navigate("ActionAlertDetails", {
content: Entity.Alert,
id: Entity.CampaignId,
title: Entity.Headline,
});
}}
/>
</SwippableCard>
);
}
if (Type === 'ARTICLE') {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<ArticleFeedCard
content={Entity}
style={styles.push}
mainActionButtonPress={() => this._gotoArticle(Entity)}
/>
</SwippableCard>
);
}
//prettier-ignore
if (Type === 'NOTIFICATION' && Entity.Code === 'INDIVIDUAL_ADDRESS_HOME_MISSING') {
return (
<MissingAddressCard
style={styles.push}
navigate={() => this.props.navigation.navigate('HomeAddress')}
/>
);
}
if (['PREFERENCE_TOPIC', 'PREFERENCE_INVOLVEMENT'].includes(Type)) {
return (
<SwippableCard onSwipedOut={() => this._onSwipedOut(item, index)}>
<GetInvolvedFeedCard
style={styles.push}
title={Entity.DisplayText}
onPress={response => this._handleGetInvolved(response, Entity)}
/>
</SwippableCard>
);
}
return null;
};
_onRefresh = async () => {
try {
this.setState({refreshing: true});
this.props
.fetchFeed()
.then(() => {
this.setState({refreshing: false});
})
.catch(() => {
this.setState({refreshing: false});
});
} catch (e) {
this.setState({refreshing: false});
}
};
_trackScroll = async event => {
try {
if (this.prompted) {
return;
}
const y = event.nativeEvent.contentOffset.y;
const scrollHeight = height * 0.8;
const page = Math.round(Math.floor(y) / scrollHeight);
const alert = await cache.shouldPromtpPushNotificationPermissions();
const iOS = Platform.OS === 'ios';
if (alert && iOS && page > 1) {
this.prompted = true;
this._openPromptAlert();
}
} catch (e) {
return false;
}
};
_openPromptAlert = () => {
Alert.alert(
'Push Notifications Access',
'Stay engaged with NFIB on the issues and activities you care about by allowing us to notify you using push notifications',
[
{
text: 'Deny',
onPress: () => {
cache.pushNotificationsPrompted();
},
style: 'cancel',
},
{
text: 'Allow',
onPress: () => {
OneSignal.registerForPushNotifications();
cache.pushNotificationsPrompted();
},
},
],
{cancelable: false}
);
};
_getAlertTitle = () => {
const {lastSwippedElement} = this.props;
const {Type} = lastSwippedElement.swippedItem;
if (Type.startsWith('PREFERENCE')) {
return 'Preference Dismissed';
}
switch (Type) {
case 'EVENT':
return 'Event Dismissed';
case 'SURVEY_BALLOT':
return 'Ballot Dismissed';
case 'SURVEY_SURVEY':
return 'Survey Dismissed';
case 'SURVEY_MICRO':
return 'Micro Survey Dismissed';
case 'ARTICLE':
return 'Article Dismissed';
case 'ALERT':
return 'Action Alert Dismissed';
default:
return 'Dismissed';
}
};
render() {
if (this.props.loading && !this.state.refreshing) {
return <Loading />;
}
const contentStyles =
this.props.feed.length > 0 ? styles.content : emptyStateStyles.container;
return (
<View style={styles.container}>
<FlatList
contentContainerStyle={contentStyles}
showsVerticalScrollIndicator={false}
data={this.props.feed}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
removeClippedSubviews={false}
onRefresh={this._onRefresh}
refreshing={this.state.refreshing}
ListEmptyComponent={() => (
<EmptyState navigation={this.props.navigation} />
)}
scrollEventThrottle={100}
onScroll={this._trackScroll}
/>
{this.props.displayAlert && (
<BottomAlert
title={this._getAlertTitle()}
onPress={this.props.undoSwipeAction}
hideAlert={this.props.hideUndoAlert}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
content: {
paddingHorizontal: scale(8),
paddingTop: scale(16),
paddingBottom: scale(20),
},
push: {
marginBottom: 16,
},
});
const mapState2Props = ({
activityFeed,
auth: {userEmailIsValidForVoterVoice},
navigation,
}) => {
return {
...activityFeed,
userEmailIsValidForVoterVoice,
loading: activityFeed.loading || navigation.deepLinkLoading,
};
};
export default connect(mapState2Props, {
fetchFeed,
getCampaignDetails,
handleUpdateGetInvoved,
handleUpdateTopics,
setSelectedAlert,
setSelectedArticle,
setSelectedEvent,
setSelectedSurvey,
handleContentSwipe,
undoSwipeAction,
hideUndoAlert,
})(ActivityFeed);
I don't see anything apparent with my code and I am wondering if it's some change that the react-navigation team did.
I am using react-navigation version 1.5.11 with react-native version 0.60.4.
Is this a compatibility issue with the RN version? Do I have no choice but to upgrade?
And this problem seems prevalent throughout my application. I also get the error here:
This is the src/auth/screens/ResetLinkConfirmationAlert.js file:
import React from 'react';
import {connect} from 'react-redux';
import ResetPasswordLinkConfirmationAlert from 'auth/components/ResetPasswordLinkConfirmationAlert';
import PropTypes from 'prop-types';
const ResetLinkConfirmationAlert = ({resetEmail, navigation}) => {
const {params} = navigation.state;
return <ResetPasswordLinkConfirmationAlert email={resetEmail} {...params} />;
};
ResetLinkConfirmationAlert.propTypes = {
navigation: PropTypes.object,
resetEmail: PropTypes.string,
};
const mapStateToProps = ({registrations}) => {
const {resetEmail} = registrations.resetPasswordData;
const email = resetEmail || registrations.verificationEmail;
return {resetEmail: email};
};
export default connect(mapStateToProps)(ResetLinkConfirmationAlert);
and it's being imported here in src/navigation/auth/stack.js:
import React from "react";
import { StackNavigator, NavigationActions } from "react-navigation";
import { Intro } from "auth/screens/Intro";
import { Login } from "auth/screens/Login";
import { PasswordReset } from "auth/screens/PasswordReset";
import { RegisterNoEmail } from "auth/screens/RegisterNoEmail";
import AskForMembership from "auth/screens/AskForMembership";
import { CompleteAccount } from "auth/screens/CompleteAccount";
import { ConfirmMemberAccount } from "auth/screens/ConfirmMemberAccount";
import { Register } from "auth/screens/Register";
import SetNewPassword from "auth/screens/SetNewPassword";
import { RegisterEmailPassword } from "auth/screens/RegisterEmailPassword";
import ResetLinkConfirmationAlert from "auth/screens/ResetLinkConfirmationAlert";
import DetailsConfirmation from "auth/screens/DetailsConfirmation";
import AccountCreated from "auth/screens/AccountCreated";
import BackButton from "navigation-components/BackButton";
import CustomHeader from "navigation-components/CustomHeader";
import HeaderTitle from "navigation-components/HeaderTitle";
import { v2Colors } from "theme";
import { defaultStackConfig, defaultHeaderStyles } from "../config";
const leftRegiterNavOptions = {
title: "Register",
headerStyle: defaultStackConfig.authHeaderStyle
};
const stack = StackNavigator(
{
Intro: {
screen: Intro,
navigationOptions: {
header: null
}
},
Register: {
screen: Register,
navigationOptions: ({ navigation }) => ({
header: <CustomHeader onPress={() => navigation.goBack(null)} />,
headerStyle: defaultStackConfig.authHeaderStyle
})
},
RegisterNoEmail: {
screen: RegisterNoEmail,
navigationOptions: leftRegiterNavOptions
},
RegisterEmailPassword: {
screen: RegisterEmailPassword,
navigationOptions: leftRegiterNavOptions
},
AskForMembership: {
screen: AskForMembership,
navigationOptions: {
header: <HeaderTitle />,
headerStyle: defaultStackConfig.authHeaderStyle
}
},
ConfirmMemberAccount: {
screen: ConfirmMemberAccount,
navigationOptions: ({ navigation }) => ({
header: (
<HeaderTitle
headerLeft={() => (
<BackButton onPress={() => navigation.goBack(null)} />
)}
/>
),
headerStyle: defaultStackConfig.authHeaderStyle
})
},
CompleteAccount: {
screen: CompleteAccount,
navigationOptions: {
header: <HeaderTitle />,
headerStyle: defaultStackConfig.authHeaderStyle
}
},
Login: {
screen: Login,
navigationOptions: ({ navigation }) => ({
title: "Log In",
headerLeft: <BackButton onPress={() => navigation.goBack(null)} />,
headerStyle: defaultStackConfig.authHeaderStyle
})
},
PasswordReset: {
screen: PasswordReset,
navigationOptions: ({ navigation }) => ({
title: "Password Reset",
headerLeft: <BackButton onPress={() => navigation.goBack(null)} />,
headerStyle: defaultStackConfig.authHeaderStyle
})
},
ResetLinkConfirmationAlert: {
screen: ResetLinkConfirmationAlert,
navigationOptions: ({ navigation }) => ({
title: "Password Reset",
headerLeft: (
<BackButton
onPress={() => {
const resetNavAction = NavigationActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: "Intro" })]
});
navigation.dispatch(resetNavAction);
}}
/>
),
headerStyle: defaultStackConfig.authHeaderStyle
})
},
Upgrading to react-navigation 2.0.0 is not the answer because I already tried that and if you are going to suggest upgrading to 3.x please explain how that will solve this issue.
It was suggested that in the changelog for react-redux 7.1.0 they mention a note stating PropTypes.func has to be changed to PropTypes.elementType if an element is being passed as a prop
github.com/reduxjs/react-redux/releases/tag/v7.0.1
So in the case where I am getting the error for SetNewPassword, I refactored it like so:
export class CompleteAccount extends PureComponent {
static propTypes = {
loading: PropTypes.bool,
newConfirmResetPassword: PropTypes.string,
newResetPassword: PropTypes.string,
resetUserPassword: PropTypes.elementType.isRequired,
setConfirnResetPassword: PropTypes.elementType.isRequired,
setNewResetPassword: PropTypes.elementType.isRequired,
validationErrors: PropTypes.object
};
and then in navigation/auth/stack.js I added curly braces to the import statement like so:
import { SetNewPassword } from "auth/screens/SetNewPassword";
but I am still getting that error message, although I am not sure if I applied that correctly. At the same time I have noticed that SetNewPassword.js file only has the named export of CompleteAccount:
export class CompleteAccount extends PureComponent {
static propTypes = {
loading: PropTypes.bool,
newConfirmResetPassword: PropTypes.string,
newResetPassword: PropTypes.string,
resetUserPassword: PropTypes.elementType.isRequired,
setConfirnResetPassword: PropTypes.elementType.isRequired,
setNewResetPassword: PropTypes.elementType.isRequired,
validationErrors: PropTypes.object
};
.......
export default connect(
mapStateToProps,
{
resetUserPassword,
setNewResetPassword,
setConfirnResetPassword
}
)(CompleteAccount);
Not sure how this file was working before in that manner. I usually name my files the same name as the class or functional screen and import it with the same name.
On further inspection I see that there are two screens with the same class name function.
CompleteAccount.js:
export class CompleteAccount extends PureComponent {
static propTypes = {
cellPhone: PropTypes.string,
cellPhoneChanged: PropTypes.func.isRequired,
city: PropTypes.string,
cityChanged: PropTypes.func.isRequired,
errors: PropTypes.object,
firstName: PropTypes.string.isRequired,
homeAddress: PropTypes.string,
homeAddressChanged: PropTypes.func.isRequired,
homePhone: PropTypes.string,
homePhoneChanged: PropTypes.func.isRequired,
registeredUser: PropTypes.object,
registerUser: PropTypes.func.isRequired,
state: PropTypes.string,
stateChanged: PropTypes.func.isRequired,
zipCode: PropTypes.string.isRequired,
zipCodeChanged: PropTypes.func.isRequired,
};
which is exported as:
export default connect(mapStateToProps, {
cityChanged,
homeAddressChanged,
homePhoneChanged,
cellPhoneChanged,
stateChanged,
zipCodeChanged,
registerUser,
})(CompleteAccount);
and then there is SetNewPassword.js:
which is also named:
export class CompleteAccount extends PureComponent {
static propTypes = {
loading: PropTypes.bool,
newConfirmResetPassword: PropTypes.string,
newResetPassword: PropTypes.string,
resetUserPassword: PropTypes.func.isRequired,
setConfirnResetPassword: PropTypes.func.isRequired,
setNewResetPassword: PropTypes.func.isRequired,
validationErrors: PropTypes.object
};
.....
export default connect(
mapStateToProps,
{
resetUserPassword,
setNewResetPassword,
setConfirnResetPassword
}
)(CompleteAccount);
even though the file name is completely different. That is confusing, why didn't they just give the second one the class name of SetNewPassword?
The immediate issue looks like the multiple exports in the component files. Try removing the export before the class definition and only keep export default at the end.
Regarding the confusion about SetNewPassword.js and CompleteAccount.js having the same exports, that'll not cause an issue as long you import the default exported component.
To put it simply,
If you export a component as default, then you can import it without the {} curly braces, like
import CompleteAccount from '.../CompleteAccount.js'
Here you can name the import anything you want.
If you use the curly braces, that'll import the named export, like
import {CompleteAccount} from '.../CompleteAccount.js'
After a long grueling 6 days at this, and attempting fixes that went against our understanding of using curly braces when you got named exports, I always suspected that the problem was with react-navigation because I did not mess with the react-navigation version or the codebase.
The problem is how react-navigation works or does not work with react-redux version 7.
React Navigation does not recognize the object returned by React-Redux version 7.
The solution was to downgrade to React-Redux version 5.1.1.

React-Native SwitchNavigator don't provide new props in root

I have problem with receiving new props in root stack navigator. I have 2 screens in stack navigator: list and edit item. On list screen i click a edit button and dispatch data to store - it works. But in the edit screen i edit data and dispatch new list with new element (for test). List screen dont receive new list. Can you help me?
App.js
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: action.data
};
case 'SET_CATEGORY_INFO':
return {
...state,
categoryInfo: action.data
};
default:
return state;
}
};
const store = createStore(reducer);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppNavigator/>
</Provider>
);
}
}
AppNavigator.js
import React from 'react';
import { createSwitchNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
import { connect } from 'react-redux';
const AppNavigator = createSwitchNavigator({
Main: MainTabNavigator
});
export default AppNavigator;
MainTabNavigator.js
import React from 'react';
import {Platform} from 'react-native';
import {createStackNavigator, createBottomTabNavigator} from 'react-navigation';
import { connect } from 'react-redux';
...
const CategoriesStack = createStackNavigator({
CategoriesListScreen: {
screen: CategoriesListScreen,
},
CategoryInfoScreen: {
screen: CategoryInfoScreen,
},
CategoryEditScreen: {
screen: CategoryEditScreen,
},
});
CategoriesStack.navigationOptions = {
tabBarLabel: 'Categories',
tabBarIcon: ({focused}) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? 'ios-link' : 'md-link'}
/>
),
};
...
const bottomTabNavigator = createBottomTabNavigator({
CategoriesStack,
...
});
export default bottomTabNavigator;
CategoriesListScreen.js
import { connect } from 'react-redux';
class CategoriesListScreen extends React.Component {
render() {
const cats = this.state.categories;
return (
<ScrollView style={styles.container}>
{cats.map((category, i) => {
return (
<TouchableOpacity key={category.id} style={
(i === cats.length - 1) ?
{...styles.categoryItem, ...styles.categoryItemLast} :
styles.categoryItem
} onPress={()=>{this.onPressCategory(category)}}>
<View style={{
...styles.categoryLabel, ...{
backgroundColor: category.color
}
}}>
<Icon name={category.icon} size={25} style={styles.categoryIcon}
color={category.iconColor}/>
</View>
<Text>{category.title}</Text>
</TouchableOpacity>
)
})}
</ScrollView>
);
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
}
componentWillMount() {
const categories = this.props.categories;
this.setState({
categories: categories
});
}
onPressCategory(category) {
this.props.setCategoryInfo(category);
this.props.navigation.navigate('CategoryInfoScreen', {});
}
}
function mapStateToProps(state) {
return {
categories: state.categories
}
}
function mapDispatchToProps(dispatch) {
return {
setCategoryInfo: (category) => dispatch({ type: 'SET_CATEGORY_INFO', data: category })
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoriesListScreen)
CategoryEditScreen.js
import { connect } from 'react-redux';
class CategoryEditScreen extends React.Component {
static navigationOptions = ({navigation}) => {
return {
title: 'Edit Category',
headerRight: <Button onPress={() => {navigation.getParam('categoryChangedDispatch')()}} title="Save"/>
}
};
render() {
const category = this.state.category;
...
}
componentWillMount() {
const category = this.props.categoryInfo;
this.setState({
category: category
});
}
componentDidMount() {
this.props.navigation.setParams({
categoryChangedDispatch: this.categoryChangedDispatch.bind(this)
});
}
categoryChangedDispatch() {
let cats = this.props.categories;
cats.push({
id: 3,
title: 'My third category',
color: '#7495e7',
icon: 'airplane',
iconColor: '#2980B9'
});
this.props.categoryChanged(cats);
this.props.navigation.navigate('CategoryInfoScreen', {});
}
}
function mapStateToProps(state) {
return {
categories: state.categories,
categoryInfo: state.categoryInfo
}
}
function mapDispatchToProps(dispatch) {
return {
categoryChanged: (categories) => dispatch({ type: 'CATEGORIES_CHANGED', data: categories }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryEditScreen)
It seems it's related to the fact you update your local state (this.state.categories) based on the categories property, only during the componentWillMount phase but not when props are updated (which should happen after you dispatched the new data).
Try using this.props.categories directly within your CategoriesListScreen component.
before
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: action.data
};
default:
return state;
}
};
after
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'CATEGORIES_CHANGED':
return {
...state,
categories: Object.assign([], action.data)
};
default:
return state;
}
};
Reducer has problem. I made the absolutely new array. It works! But I think it isn't normal :)

Unrecognized font family 'entypo'

I'm using the create react native app by the expo team to build an app. Using Icon component from react-native-elements to create a react navigation header feature. Snippet below:
const Navigator = new createStackNavigator({
Home: {
screen: Home,
path: '/',
navigationOptions: ({ navigation }) => ({
title: 'Home',
headerStyle: {
backgroundColor: 'black'
},
headerLeft: (
<Icon
name="menu"
size={30}
type="entypo"
style={{ paddingLeft: 10 }}
/>
),
}),
},
})
I encountered this error:
After numerous iterations, I found this supposed work around 1st and 2nd by the expo team and implemented it this way below for the app but still encountering the same problems.
import Expo from "expo";
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font } from 'expo';
import { FontAwesome, Ionicons } from '#expo/vector-icons';
import { connect } from 'react-redux'
import { Auth } from 'aws-amplify';
import AuthTabs from './auth/Tabs';
import Nav from './navs/Navigator';
import Home from "./components/Home";
class App extends React.Component {
state = {
user: {},
isLoading: true,
isLoadingComplete: false,
};
async componentDidMount() {
StatusBar.setHidden(true)
try {
const user = await Auth.currentAuthenticatedUser()
this.setState({ user, isLoading: false })
} catch (err) {
this.setState({ isLoading: false })
}
}
async componentWillReceiveProps(nextProps) {
try {
const user = await Auth.currentAuthenticatedUser()
this.setState({ user })
} catch (err) {
this.setState({ user: {} })
}
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return(
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
}
else{
if (this.state.isLoading) return null
let loggedIn = false
if (this.state.user.username) {
loggedIn = true
}
if (loggedIn) {
return (
<Nav />
)
}
return (
<AuthTabs />
)
}
}
_loadResourcesAsync = async () => {
console.log("fonts loading..")
const entypoFont = {
'entypo': require('../node_modules/#expo/vector-icons/fonts/Entypo.ttf')
};
const fontAssets = cacheFonts([ FontAwesome.font, Ionicons.font, entypoFont ]);
console.log("loaded all fonts locally")
await Promise.all([...fontAssets]);
console.log("promisified all fonts")
};
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
function cacheFonts(fonts){
return fonts.map(font => Font.loadAsync(font))
}
const mapStateToProps = state => ({
auth: state.auth
})
export default connect(mapStateToProps)(App)
What are my doing wrong and how can it be configured appropriately? Thank you

React-Native/React navigation redirection after login with redux

I have a problem during the login my redirection is not done, I use a rail API for the back, I can recover the user, but when I click on login the page reloads and does not navigate on the home screen.
In my AuthActions.js after the dispatch I added NavigationActions.navigate ('Home'), this should allow me to go to the home page
AppNavigator.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addNavigationHelpers, StackNavigator, TabNavigator } from 'react-navigation';
import { addListener } from '../../utils/redux';
import LoginScreen from '../login/LoginForm';
import RegisterScreen from '../register/RegisterForm';
import LogoutScreen from '../logout/Logout';
import Home from '../home/HomeList';
import Hair from '../hair/HairList';
export const Tabs = TabNavigator({
screen1: {
screen: Home
}
});
export const AppNavigator = StackNavigator({
Login: { screen: LoginScreen },
Main: { screen: Home },
Hair: { screen: Hair },
},{
initialRouteName : 'Login'
});
class AppWithNavigationState extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
nav: PropTypes.object.isRequired,
};
render() {
const { dispatch, nav } = this.props;
return (
<AppNavigator
navigation={addNavigationHelpers({
dispatch,
state: nav,
addListener,
})}
/>
);
}
}
const mapStateToProps = state => ({
nav: state.nav,
});
export default connect(mapStateToProps)(AppWithNavigationState);
AuthReducers.js
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGIN_USER
} from '../actions/types';
const INITIAL_STATE = {
email: '',
password: '',
user: null,
error: '',
loading: false,
};
export default (state = INITIAL_STATE, action) => {
// console.log('action =>', action);
switch (action.type) {
case EMAIL_CHANGED:
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
case LOGIN_USER:
return { ...state, loading: true, error: '' };
case LOGIN_USER_SUCCESS:
return { ...state, ...INITIAL_STATE, user: action.payload };
case LOGIN_USER_FAIL:
return { ...state, error: 'Authentication Failed.', password: '', loading: false };
default:
return state;
}
};
// newState === oldState ? nothing : update state, so component too
AuthActions.js
import { NavigationActions } from 'react-navigation';
import Api from '../api';
import User from '../models/User';
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGIN_USER
} from './types';
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text
};
};
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
};
export const loginUser = ({ email, password }) => {
return (dispatch) => {
dispatch({ type: LOGIN_USER });
Api.userSignUp(email, password)
.then((user) => loginUserSuccess(dispatch, user))
.catch((error) => {
console.log(error);
loginUserFail(dispatch);
});
};
};
const loginUserFail = (dispatch) => {
dispatch({
type: LOGIN_USER_FAIL
});
};
const loginUserSuccess = (dispatch, user) => {
// console.log('access token =>', User.getAccessToken());
// Api.createEmployee(null);
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user,
});
NavigationActions.navigate('home'); //HERE NOT REDIRECT AFTER SUCCESS TO HOME
};
LoginForm.js
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import { emailChanged, passwordChanged, loginUser } from '../../actions';
import { Card, CardSection, Input, Button, Spinner } from '../common';
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
onPasswordChange(text) {
this.props.passwordChanged(text);
}
onButtonPress() {
const { email, password } = this.props;
this.props.loginUser({ email, password });
}
renderButton() {
if (this.props.loading) {
return <Spinner size="large" />;
}
return (
<Button onPress={this.onButtonPress.bind(this)}>
Login
</Button>
);
}
render() {
return (
<Card>
<CardSection>
<Input
label="Email"
placeholder="email#gmail.com"
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
</CardSection>
<CardSection>
<Input
secureTextEntry
label="Password"
placeholder="password"
onChangeText={this.onPasswordChange.bind(this)}
value={this.props.password}
/>
</CardSection>
<Text style={styles.errorTextStyle}>
{this.props.error}
</Text>
<CardSection>
{this.renderButton()}
</CardSection>
</Card>
);
}
}
const styles = {
errorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
}
};
const mapStateToProps = ({ auth }) => {
const { email, password, error, loading } = auth;
return { email, password, error, loading };
};
export default connect(mapStateToProps, {
emailChanged, passwordChanged, loginUser
})(LoginForm);
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import AppReducer from './reducers';
import AppWithNavigationState from './components/navigator/AppNavigator';
import { middleware } from './utils/redux';
import User from './models/User.js';
import {
NavigationActions,
} from 'react-navigation';
const store = createStore(
AppReducer,
applyMiddleware(ReduxThunk),
);
class App extends Component {
state = { isLoggedIn: null }
componentWillMount() {
// Si le asyncstorage a changé (connexion, etc)
User.getCurrent().then((user) => {
console.log('GetCurrentUser =>', user);
if (user) {
this.setState({isLoggedIn: true});
this.props.navigation.navigate('Main', user);
}
else {
this.setState({isLoggedIn: false});
}
})
}
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
export default App;
I'm blocking for some day on the worries, I still is not finding a solution
use this code NavigationActions.NAVIGATE("home") works for me
You have to use componentWillReceiveProps in your Home like this:
//Home
componentWillReceiveProps(nextProps) {
if(nextProps.user) {
this.props.navigation.navigate('Home');
}
}
LoginForm.js
const mapStateToProps = state => {
return {
error: state.auth.error,
loading: state.auth.loading,
user: state.auth.user,
}
}
And you edit your AuthReducers.js like this:
case LOGIN_USER_SUCCESS:
return { ...state, ...INITIAL_STATE, user: action.user};
It works for me!