javascriptreactjsaxiosisomorphic-fetch-api

ReactJS Cannot read property setState of undefined


I am new to ReactJS, and somewhat understand that this question is duplicated to numeral questions in SOF.

But I hope someone can give me some directions and clarifications on some concepts of React.

What I want to do is simply get data from remote REST API using axios or isomorphic-fetch before the screen is loaded.

However, I saw quite a lot of similar React tutorials, I still did not get the point.

Here is my code:

import React, { Component } from 'react';
import axios from 'axios'

// import InputRow from './components/InputRow';
import './App.css';

// require('es6-promise').polyfill();
// require('isomorphic-fetch');

class App extends Component {

	constructor(props) {
		super(props);

		const app_id = '<my_app_id>';

		const version = 0.1;
		this.luisURL = `https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/${app_id}/versions/${version}`;

		// this.getLUISIntent = this.getLUISIntent.bind(this);

		this.state = {
			intentOptions: [],
			utterance: '',
			intent: ''
		};
	}

	getLUISIntent = () => {
		const subscription_key = '<my_subscription_key>';

		const URL = this.luisURL + '/intents';

		return axios.get(URL, {
			headers: {
				"Content-Type": 'application/json',
				"Ocp-Apim-Subscription-Key": subscription_key
			},
		})
		.then(function(res){
			this.setState({intentOptions: res.data});
		});
	}

	componentWillMount = () => {
		// this.setState({
		// 	intentOptions: this.getLUISIntent()
		// });
	}

	componentDidMount = () => {
		this.getLUISIntent();
	}

	componentDidUpdate = (prevProps, prevState) => {
		console.log("state: ", this.state);
		// this.submitToLUIS();
	}

	shouldComponentUpdate = (nextProps, nextState) => {
		return true;
	}

	submitToLUIS = () => {
		console.log("Submit to LUIS: ", this.state);
	};

	setIntent = (e) => {
		this.setState({intent: e.target.value});
	};

	setUtterance = (e) => {
		this.setState({utterance: e.target.value});
	};

	render() {
		return (
			<div className="App">
				<div className="container">
					<div className="row">
						<div className="col-sm-4">Utterance</div>
						<div className="col-sm-4">Intent</div>
						<div className="col-sm-4"></div>
					</div>
					<div className="row">
						<div className="col-sm-4">
							<input className="form-control utterance" type="text" onChange={this.setUtterance} />
						</div>
						<div className="col-sm-4">
							<select className="form-control intent" onChange={this.setIntent}>
								<option key="intent_defualt" value="">Select...</option>
								{this.state.intentOptions.map((obj, i) =>
									<option key={obj.id} value={obj.id}>{obj.name}</option>
								)}
							</select>
						</div>
						<div className="col-sm-4">
							<button className="btn btn-primary" onClick={this.submitToLUIS}>Create</button>
						</div>
					</div>
				</div>
      		</div>
    	);
  	}
}

export default App;

Here are my questions:

Thank you very much in advance for your kind help.


Solution

  • your callback function from the ajax request is not bound. When you pass a function to another function (as a callback), "this" will be a reference to the context it is in when it is finally called, as opposed to what it is when you wrote it. If you use an arrow function, it will keep the context it had when you wrote it.

    .then(function(res){
            this.setState({intentOptions: res.data});
        });
    

    If you make this function an arrow function, the issue should be resolved.

    .then((res) => this.setState({intentOptions: res.data}));