Error: TypeError: undefined is not an object (evaluating ‘this.props.history.push’)
I am probably not passing history correctly!?
_
My code:
Login.tsx
import {
IonPage,
IonContent,
IonList,
IonItem,
IonLabel,
IonInput,
IonButton,
IonImg,
IonAlert,
IonSpinner
} from '@ionic/react';
import React from 'react';
import { RouteComponentProps } from 'react-router';
import { InputChangeEventDetail } from '@ionic/core';
import fetch, {
FetchData,
FetchError,
InvalidResponseError
} from '../ormon';
import { store } from '../utils/Store';
type LoginState = {
isLoading: boolean,
error: FetchError | null,
username: string,
password: string,
twoFACode: string,
twoFANeedsSMSCode: boolean,
twoFANeedsOTPCode: boolean
};
import { useHistory } from 'react-router';
export default class Login extends React.PureComponent<RouteComponentProps, LoginState> {
constructor(props: RouteComponentProps) {
super(props);
this.state = {
isLoading: false,
error: null,
username: "",
password: "",
twoFACode: "",
twoFANeedsSMSCode: false,
twoFANeedsOTPCode: false
};
}
updateUsername = (event: CustomEvent<InputChangeEventDetail>) => {
this.setState({ username: event.detail.value ? event.detail.value.trim() : "" });
}
updatePassword = (event: CustomEvent<InputChangeEventDetail>) => {
this.setState({ password: event.detail.value ? event.detail.value.trim() : "" });
}
updateTwoFACode = (event: CustomEvent<InputChangeEventDetail>) => {
this.setState({ twoFACode: event.detail.value ? event.detail.value.trim() : "" });
}
componentDidMount() {
console.log('componentDidMount');
store.clear();
}
render () {
return (
<IonPage>
<IonContent className="ion-padding ion-justify-content-end"
scrollX={ false }
scrollY={ false }
style={{
maxWidth: '480px',
alignSelf: 'center'
}}>
<form style={{
position: 'relative',
top: '50%',
transform: 'translateY(-50%)'
}} onSubmit={ this.doLogin }>
<IonList>
{ !this.state.twoFANeedsSMSCode && ! this.state.twoFANeedsOTPCode &&
<div>
<IonItem>
{/*<IonLabel position="floating">Benutzername</IonLabel>*/}
<IonInput label="Benutzername" name="username"
required
clearInput
autofocus
autocapitalize="characters"
value={ this.state.username }
onIonChange={ this.updateUsername } />
</IonItem>
<IonItem>
{/*<IonLabel position="floating">Passwort</IonLabel>*/}
<IonInput label="Passwort" name="password"
type="password"
required
clearInput
clearOnEdit
value={ this.state.password }
onIonChange={ this.updatePassword } />
</IonItem>
</div>
}
{ this.state.twoFANeedsSMSCode &&
<IonItem>
<IonLabel position="floating">SMS Code</IonLabel>
<IonInput name="sms"
placeholder="abcd.efgh"
autofocus
required
value={ this.state.twoFACode }
onIonChange={ this.updateTwoFACode } />
</IonItem>
}
{ this.state.twoFANeedsOTPCode &&
<IonItem>
<IonLabel position="floating">Authenticator App Code</IonLabel>
<IonInput name="otp"
placeholder="XXX XXX"
autofocus
required
value={ this.state.twoFACode }
onIonChange={ this.updateTwoFACode } />
</IonItem>
}
</IonList>
{ !this.state.twoFANeedsSMSCode && !this.state.twoFANeedsOTPCode &&
<IonButton expand="block" type="submit" style={{ marginTop: '15px' }}>Login</IonButton>
}
{ (this.state.twoFANeedsSMSCode || this.state.twoFANeedsOTPCode) &&
<div>
<IonButton expand="block" color="medium" onClick={ this.doReset } style={{ marginTop: '15px', float: 'left' }}>Zurück</IonButton>
<IonButton expand="block" type="submit" style={{ marginTop: '15px', float: 'right' }}>Weiter</IonButton>
</div>
}
</form>
</IonContent>
{ this.state.error && <IonAlert isOpen={ true }
header={ this.state.error.title ? this.state.error.title : this.state.error.name }
message={ this.state.error.message }
buttons={[{ text: "Ok" }]}
onDidDismiss={ () => { this.setState({ error: null }); } } />
}
{ this.state.isLoading && <div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
top: 0, right: 0, bottom: 0, left: 0,
zIndex: 100,
backgroundColor: 'white',
opacity: 0.5
}}><IonSpinner color="dark" /></div> }
</IonPage>
);
}
doReset = (event: any) => {
console.log('doReset');
store.clear();
this.setState({
isLoading: false,
error: null,
username: "",
password: "",
twoFACode: "",
twoFANeedsSMSCode: false,
twoFANeedsOTPCode: false
});
};
doLogin = (event: React.FormEvent<HTMLFormElement>) => {
console.log('doLogin');
event.preventDefault();
this.setState({ isLoading: true });
if(store.userSessionKey && !this.state.twoFANeedsSMSCode && !this.state.twoFANeedsOTPCode){
console.log('store.clear();');
store.clear();
}
if(!store.userSessionKey){
console.log('!store.userSessionKey');
fetch(store.apiURL, {
trtyp: "lon004",
uid: this.state.username,
pw1: this.state.password,
slicd: "",
remad: store.deviceIdentifier,
trnsuid: this.state.username,
trnspw1: this.state.password
}).then(response => {
console.log('then 4');
const data = response.body as FetchData;
if( !data || !data.seskey ){
throw new InvalidResponseError("Session key is missing from response.");
}
store.userSessionKey = data.seskey;
if (data.indchp !== '0') {
console.log('data.indchp !== 0');
/** The user needs to change password. This can't be done in the mobile app for now,
* so we simply throw an exception and abort the login process: */
throw new InvalidResponseError("Password expired: You need to set a new password by logging in from your desktop.");
} else if (data.indsms === '1') {
console.log('data.indsms === 1');
// 2FA: Method possibly changed from OTP to SMS, so we require the OTP Code next time:
store.twoFASkipOTPCode = false;
// 2FA: SMS is enabled
this.setState({ isLoading: false, twoFANeedsSMSCode: true });
} else if (data.indqrc === '1' && !store.twoFASkipOTPCode) {
console.log('data.indqrc === 1 && !store.twoFASkipOTPCode');
// 2FA: OTP (One-Time-Password) is enabled
this.setState({ isLoading: false, twoFANeedsOTPCode: true });
} else {
console.log('ret this.loadData()');
return this.loadData();
}
}).catch(error => {
console.log('catch err');
this.setState({
isLoading: false,
error: error
});
});
} else if(this.state.twoFANeedsSMSCode || this.state.twoFANeedsOTPCode) {
console.log('this.state.twoFANeedsSMSCode || this.state.twoFANeedsOTPCode');
if(!this.state.twoFACode || !this.state.twoFACode.trim()) {
this.setState({
isLoading: false,
error: new FetchError("Please enter the " + (this.state.twoFANeedsSMSCode ? 'SMS' : 'authenticator app') + " code.")
});
return;
}
fetch(store.apiURL, {
trtyp: "lon005",
seskey: store.userSessionKey,
remad: store.deviceIdentifier,
code3: this.state.twoFACode.trim()
}).then(() => {
console.log('then case 3');
// 2FA: Only ask for OTP Code for the very first time
if(this.state.twoFANeedsOTPCode) {
console.log('then case 3 store.twoFASkipOTPCode = true');
store.twoFASkipOTPCode = true;
}
return this.loadData();
}).catch(error => {
this.setState({
isLoading: false,
error: error
});
});
}
};
loadData = async () => {
console.log('ƒ loadData');
return fetch(store.apiURL, {
trtyp: "adm1034",
trtypalt: "adm1034m",
seskey: store.userSessionKey,
indspc1: 1,
indsuts: 1,
usrorg: 0,
remad: store.deviceIdentifier,
pos: 0,
nrc: 9999,
kndsb: '00'
}).then(response => {
console.log('then case 1');
const data = response.body as FetchData;
store.orgCCY = data.orgCCY;
// check if single client
return fetch(store.apiURL, {
trtyp: "adm1034",
trtypalt: "adm1034z",
seskey: store.userSessionKey,
remad: store.deviceIdentifier,
pos: 0,
nrc: 9999
})
}).then(response => {
console.log('then case 2');
const data = response.body as FetchData[];
if( !data ){
throw new InvalidResponseError("Data is not an array.");
}
console.log('checking this', this);
console.log('checking this.props', this.props);
if(data.length > 1){
store.isSingleLogin = false;
console.log('isSingleLogin false');
this.props.history.push('/main/client-list');
} else {
store.isSingleLogin = true;
console.log('isSingleLogin true');
this.props.history.push('/main');
}
this.setState({
isLoading: false,
username: "",
password: "",
twoFACode: "",
twoFANeedsSMSCode: false,
twoFANeedsOTPCode: false
});
});
};
}
App.tsx
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { IonApp, IonRouterOutlet } from '@ionic/react';
import Main from './pages/Main';
import Login from './pages/Login';
import Cash from './pages/Cash';
import CashDetail from './pages/CashDetail';
import Securities from './pages/Securities';
import SecurityDetail from './pages/SecurityDetail';
import SecurityAmountDetail from './pages/SecurityAmountDetail';
import ClientList from './pages/ClientList';
import Totals from './pages/Totals';
import ReportPerformanceAdHoc from './pages/ReportPerformanceAdHoc';
import { store } from './utils/Store';
import '@ionic/core/css/core.css';
import '@ionic/core/css/ionic.bundle.css';
/* Theme variables */
import './theme/variables.css';
/* Internationalization Support */
import { IntlProvider } from 'react-intl';
import { IonReactRouter } from '@ionic/react-router';
export default class App extends React.PureComponent<{},{}> {
render (){
return (
<IntlProvider locale="de-CH">
<IonApp>
<IonReactRouter>
<IonRouterOutlet>
<Route exact path="/login" component={ Login } />
<Route exact path="/" render={() => {
if( store.userIsLoggedIn ){
return (<Redirect to="/main" />);
}
return (<Redirect to="/login" />);
}} />
<Route exact path="/main" component={ Main } />
<Route exact path="/main/client-list" component={ ClientList } />
<Route exact path="/main/cash" component={ Cash } />
<Route exact path="/main/cashdetail" component={ CashDetail } />
<Route exact path="/main/securities" component={ Securities } />
<Route exact path="/main/securitydetail" component={ SecurityDetail } />
<Route exact path="/main/securityamountdetail" component={ SecurityAmountDetail } />
<Route exact path="/main/totals" component={ Totals } />
<Route exact path="/main/report/performance-adhoc" component={ ReportPerformanceAdHoc } />
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
</IntlProvider>
);
}
}
package.json
{
"name": "myApp",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test.e2e": "cypress run",
"test.unit": "vitest",
"lint": "eslint"
},
"dependencies": {
"@amcharts/amcharts4": "^4.10.22",
"@capacitor/app": "5.0.7",
"@capacitor/core": "5.7.3",
"@capacitor/haptics": "5.0.7",
"@capacitor/keyboard": "5.0.8",
"@capacitor/status-bar": "5.0.7",
"@ionic/react": "^7.0.0",
"@ionic/react-router": "^7.0.0",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.3.3",
"ionicons": "^7.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^5.3.4",
"react-router-dom": "^5.3.4",
"react-intl": "^6.2.2"
},
"devDependencies": {
"@capacitor/cli": "5.7.3",
"@testing-library/dom": ">=7.21.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@vitejs/plugin-legacy": "^5.0.0",
"@vitejs/plugin-react": "^4.0.1",
"cypress": "^13.5.0",
"eslint": "^8.35.0",
"eslint-plugin-react": "^7.32.2",
"jsdom": "^22.1.0",
"terser": "^5.4.0",
"typescript": "^5.1.6",
"vite": "^5.0.0",
"vitest": "^0.34.6"
},
"description": "An Ionic project"
}
1 post - 1 participant