The below mentioned code causes the app to crash throwing an error : this.setState is not a function
class CowtanApp extends Component { constructor(props){ super(props); this.state = { timePassed: false }; } render() { setTimeout(function(){this.setState({timePassed: true})}, 1000); if (!this.state.timePassed){ return <LoadingPage/>; }else{ return ( <NavigatorIOS style = {styles.container} initialRoute = {{ component: LoginPage, title: 'Sign In', }}/> ); } } }
‘this’ keyword cannot be used inside a callback in setTimeout. In Javascript, if a normal function is used as a callback in setTimeout, ‘this’ keyword doesn’t point to the instance of that component. It rather points to the global scope or the object that called the function, which could be the window, the document, a button or whatever. There is no setState method available at global level which causes this issue.
To overcome this issue, 2 majorly used approaches are mentioned below:
the function needs to be binded inside this particular class using the ‘bind’ keyword as shown below.
class CowtanApp extends Component { constructor(props) { super(props); this.state = { timePassed: false }; this.handleTimeout = this.handleTimeout.bind(this); } componentDidMount() { setTimeout(this.handleTimeout, 1000); } handleTimeout() { this.setState({ timePassed: true }); } render() { if (!this.state.timePassed) { return <LoadingPage />; } else { return ( <NavigatorIOS style={styles.container} initialRoute={{ component: LoginPage, title: 'Sign In', }} /> ); } } }
Following the ES6+ standards, this problem can also be resolved by using arrow functions. In arrow functions, there is no binding of ‘this’. In Arrow functions, ‘this’ keyword always represents the object that defined the arrow function.
The solution is:
class CowtanApp extends Component { constructor(props) { super(props); this.state = { timePassed: false }; } componentDidMount() { setTimeout(() => { this.setState({ timePassed: true }); }, 1000); } render() { if (!this.state.timePassed) { return <LoadingPage />; } else { return ( <NavigatorIOS style={styles.container} initialRoute={{ component: LoginPage, title: 'Sign In', }} /> ); } } }