React cannot set property of undefined - reactjs

I'm making a get request using axios. I know for a fact that when I make a get request, I get the correct data.
I have an array (allQuotes) in my constructor. However, when I try to reference it in componentDidMount, it's undefined.
class App extends Component {
constructor() {
super();
this.allQuotes = [];
}
componentDidMount() {
axios.get("http://getquote.herokuapp.com/get")
.then(function (response) {
this.allQuotes = response.data;
console.log(response.data);
this.getNewQuote();
})
.catch(function (error) {
console.log("Error: ", error);
//console.dir(error);
});
}
}
Upon running this, the console says "Cannot set property 'allQuotes' of undefined".
Why is this undefined?

It's better if you put allQuotes in state then you use setState
class App extends Component {
constructor() {
super();
this.state = {
allQuotes: [],
}
}
componentDidMount() {
axios.get("http://getquote.herokuapp.com/get")
.then(function (response) {
this.setState({ allQuotes: response.data })
console.log(response.data);
this.getNewQuote();
})
.catch(function (error) {
console.log("Error: ", error);
//console.dir(error);
});
}

You can use arrow functions to fix this. The problem is because if its another function, this refers to the function, and arrow function doesnt have one, instead it has the this of its referer.
axios.get("http://getquote.herokuapp.com/get")
.then((response)=>{
...
})
.catch( (error)=> {
...
});

Since you are using react, please make use of state.
What is a state ?
state are both plain JavaScript objects within the component and you can use setState to store the value within the component. You can refer https://reactjs.org/docs/faq-state.html
state = {
allQuotes: []
}
componentDidMount() {
axios.get("http://getquote.herokuapp.com/get")
.then(function (response) {
//this.allQuotes = response.data;
this.setState({
allQuotes: response.data
})
console.log(response.data);
this.getNewQuote();
})
.catch(function (error) {
console.log("Error: ", error);
//console.dir(error);
});
}

Related

Getting object promise instead of value

login(loginId, password) {
return axios
.post(API_URL + "login", {
loginId,
password
})
.then(response => {
console.log(response.data);
if (response.data) {
localStorage.setItem("token", JSON.stringify(response.data));
localStorage.setItem("user", this.getUser(loginId));
console.log(localstorage.getItem("user");
}
console.log(response.data);
return response.data;
});
}
getUser(loginId){
return axios
.get(API_URL+"user/search/"+loginId,{
headers: { Authorization: `Bearer ${authHeader()} ` },
});
getCurrentUser() {
return (JSON.parse(localStorage.getItem('user')));
}
}
class ViewMytweetComponent extends Component {
constructor(props) {
super(props)
this.onChangeReply = this.onChangeReply.bind(this);
this.state = {
Tweet: [],
reply: "",
user: AuthService.getCurrentUser()
}
this.deleteTweet = this.deleteTweet.bind(this);
}
componentDidMount() {
const { user } = this.state;
console.log(user);
var userId = user.loginId;
TweetDataService.getMyTweet(userId).then((res) => {
this.setState({ Tweet: res.data });
// console.log(this.state.Tweet);
});
}
}
In the login method I call the getUser method and store its return value to localStorage with the key user. The getCurrentUser method is used to return the stored user-item from the localStorage object.
Requesting the previously stored user in the componentDidMount method however fails. Logging the user object to the console produces:
[object Promise].
Does anyone know how to solve this?
since axios.get returns a promise, the getUser method is also returning a promise too. Which is an object, when you try to save it in localStorage in here:
localStorage.setItem("user", this.getUser(loginId));
JavaScript automaticaly converts it to a string, which becomes: [object Promise].
There are a few ways to solve this, for example:
login(loginId, password) {
return axios
.post(API_URL + "login", {
loginId,
password
})
.then(response => {
console.log(response.data);
if (response.data) {
// store the result instead of the promise itself,
// also stringify the result before javascript creates a meaningless string itself.
this.getUser(loginId).then((user)=>localStorage.setItem("user", JSON.stringify(user))
localStorage.setItem("token", JSON.stringify(response.data));
console.log(response.data);
return response.data;
})
}
Of course nested thens aren't exactly a good practice, so maybe it would be nice to rethink class' overal data fetching logic.

Using setState inside axios.fetch...then() with typescript

I am using a class component with react and this error popped up. Wondering, did anyone use a this inside axios before or know how to? Thank you in advance
type State = {
fetchedPassword: string;
fetchedPassword: string;
}
type Props = {
}
export default class MyComponent extends React.Component<Props,State>() {
constructor(props: Props) {
super(props);
this.state= {
fetchedPassword: "",
fetchedUsername: ""
}
}
authLogin = (e:any) => {
e.preventDefault();
const { fetchedUsername, fetchedPassword } = this.state;
axios
.get(url)
.then(function (response) {
this.setState({ fetchedPassword: response.data.password }); //error appears here
this.setState({ fetchedUsername: response.data.username }); //and here
})
.catch(function (error) {
console.log("Error: " + error);
});
}
}
The error says
'this' implicitly has type 'any' because it does not have a type annotation.ts(2683)
MyComponenet.tsx(26, 13): An outer value of 'this' is shadowed by this container.
I'm not sure how to solve this
Make a copy of you component instance this and store it in that constant:
authLogin = (e:any) => {
e.preventDefault();
const { fetchedUsername, fetchedPassword } = this.state;
const that = this
axios
.get(url)
.then(function (response) {
that.setState({ fetchedPassword: response.data.password });
that.setState({ fetchedUsername: response.data.username });
})
.catch(function (error) {
console.log("Error: " + error);
});
}
Move authLogin outside your constructor, then, as a last line in your constructor, add
this.authLogin = this.authLogin.bind(this);
See https://www.freecodecamp.org/news/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56/ for more info.

Unable to access Api call data. Returns undefined. React

I am trying to make a movie search app with React and have made an API call to The Movie Database API. What I am trying to do is get the data of the new movie releases, but then make another API call to get the specific details for each of those new releases since that data is stored in a different location.
I am able to access the data from the first API call, but when I try to access the movie taglines from the second data object, the console outputs "Cannot read property 'tagline' of undefined".
App.js
class App extends Component {
constructor(props) {
super(props)
this.state = {
movieRows: [],
ids: [],
movieDetails: [],
}
this.performSearch = this.performSearch.bind(this);
}
componentDidMount() {
this.performSearch();
}
performSearch() {
const urlString = "https://api.themoviedb.org/3/movie/popular?api_key=6db3cd67e35336927891a72c05&language=en-US&page=1";
axios.get(urlString)
.then(res => {
const results = res.data.results
let movieRows = [];
let movieDetails = [];
results.forEach((movie) => {
movieRows.push(movie);
axios.get(`https://api.themoviedb.org/3/movie/${movie.id}?api_key=6db3cd67e35336927891a72c05&language=en-US`)
.then(res => {
movieDetails.push(res.data);
})
.catch(function (error) {
console.log(error);
});
});
this.setState({
movieRows: movieRows,
movieDetails: movieDetails,
});
})
.catch(function (error) {
console.log(error);
});
}
Content.js
export default class Content extends Component {
constructor(props) {
super(props)
this.state = {
name: 'Jonathan',
}
this.filmLoop = this.filmLoop.bind(this);
}
filmLoop() {
let movieData = this.props.globalState.movieRows;
let movieDetails = this.props.globalState.movieDetails;
return movieData.map((movie, index) => {
return (
<div className="film" key={index}>
<img className="poster" src={`http://image.tmdb.org/t/p/w342${movie.poster_path}`} alt="The Dark Knight poster" />
<div className="film-info">
<div className="film-title">
<h3>{movie.title}</h3>
</div>
<h4>{movieDetails[index].tagline}</h4>
*I get the error from the last line
Well the main issue is that you are calling setState outside your .then you have to update the state inside your then or your catch. This is because the promise is an async function, so you have to change the state only when the promise has been resolved of rejected.
performSearch() {
const urlString = "https://api.themoviedb.org/3/movie/popular?api_key=6db3cd67e35336927891a72c05&language=en-US&page=1";
axios.get(urlString)
.then(responsePopular => {
const results = responsePopular.data.results
let movieRows = [];
let movieDetails = [];
results.forEach((movie) => {
movieRows = [...movieRows, movie];
axios.get(`https://api.themoviedb.org/3/movie/${movie.id}?api_key=6db3cd67e35336927891a72c05&language=en-US`)
.then(responseMovie => {
movieDetails = [...movieDetails, responseMovie.data];
this.setState({
movieRows: movieRows,
movieDetails: movieDetails,
})
})
.catch(function (error) {
console.log(error);
});
});
})
.catch(function (error) {
console.log(error);
});
}
I think that this could solve your issue.

Display data from the response returned from axios. Reactjs

Please help me ! i am very new to reactjs
I am able to get response from web service . But i am unable to display the same on screen(mainly have to display in dropdown which i havn't tried yet as first step for me is to see the data on screen).
My webservice data :
[{"id":1,"db_name":"mysql","status":true,"urlRequired":true,"userNameRequired":true,"passwordRequired":true,"dbNameRequired":true}]
My code :-
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class FetchDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentDidMount() {
let currentComponent = this;
axios.get(`http://10.11.202.253:8080/ETLTool/getAllDBProfile`)
.then(function (response) {
console.log(response);
console.log(response.data);
currentComponent.setState({
posts: response.data.items
});
})
.catch(function (error) {
console.log(error);
});
}
render() {
const renderItems = this.state.posts.map(function(item, i) {
return <li key={i}>{item.title}</li>
});
return (
<ul className="FetchDemo">
{renderItems}
</ul>
);
}
}
export default FetchDemo;
Error :-enter image description here
My response data via axios :-
enter image description here
I would remove .items since your response won't have that every time and just handle the data your receive in a render function if needed.
You could just do a conditional check when you set your state since I guess your db could be empty at some point:
currentComponent.setState({ posts: response.data ? response.data : [] });
In the above case, the mistake is with understanding Component Lifecycle of React.
Please take a look at the reference.
ComponentDidMount occurs after the render has occurred.
Hierarchy will be
1. constructor
2. componentWillMount
3. render
4. componentDidMount
if the code is modified with
render() {
if (this.state.posts.length) {
let renderItems = this.state.posts.map(function(item, i) {
return <li key={i}>{item.title}</li>
});
}
return (
<ul className="FetchDemo">
{renderItems}
</ul>
);
}
OR
replace ComponentDidMount with ComponentWillMount
componentWillMount() {
let currentComponent = this;
axios.get(`http://10.11.202.253:8080/ETLTool/getAllDBProfile`)
.then(function (response) {
console.log(response);
console.log(response.data);
currentComponent.setState({
posts: response.data.items
});
})
.catch(function (error) {
console.log(error);
});
}
Personal preference is the first suggestion as it checks "posts" state is initialized with Array or not if it's not an Array then map function will surely through an error.
Adding to it, there might be a problem with how you are taking the response from axios too.
currentComponent.setState({
posts: response.data.items
});
as I don't think response.data.items will give any data, as it should be limited to response.data only.
currentComponent.setState({
posts: response.data
});

Get API response to a function and populate controls

I have created a react application where i am fetching an API and getting the response. below are the code,
export class EmpDetails extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.updateEmpName = this.updateEmpName.bind(this);
}
componentWillReceiveProps(nextProps) {
this.handleProp(nextProps);
if(nextProps){
this.GetData(nextProps);
} else {
console.log("Emp number not set");
}
}
GetData(props, EmpCollection) {
this.ApiCall(props);
}
ApiCall(props) {
$.ajax({
url: 'http://localhost:8081/getdata',
type: 'POST',
data: {Empnumber:props.Empnumber},
success: function(data) {
this.setState({EmpCollection: data.EmpCollection});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.Empnumber, status, err.toString());
}.bind(this)
});
}
getInitialState(){
return {
EmpCollection: []
}
}
updateEmpName(e) {
this.setState({EmpName: e.target.value});
}
render() {
return (
<form>
<div >
<input
type="text"
id="EmpName"
placeholder="Emp Name"
value={this.state.EmpName}
onChange={this.updateEmpName} />
</div>
</form>
);
}
}
I am able to get the response and can use it only in render(). and I wanted API response in GetData() or any other method so that i can set the state of there and populate controls. not in render. any idea how can i achieve this?
Well you need to save the response somewhere. I could be just variable outside of component or it could be component property. For example,
class YourComponent extends React.Component {
constructor() {
// your code ...
this.response = null;
}
callApi() {
const self = this;
$.ajax({
// your code ...
success: function(response) {
self.response = response;
}
})
}
someOtherMethod() {
console.log(this.response)
}
}
I would suggest you to make api call in life cycle method componentDidMount not in componentWillReceiveProps as recommended in react docs.
Need to change your api call method a little bit.
ApiCall(props) {
return $.ajax({
url: 'http://localhost:8081/getdata',
type: 'POST',
data: {Empnumber:props.Empnumber}
}).fail((responseData) => {
if (responseData.responseCode) {
console.error(responseData.responseCode);
}
});
}
Basically above call will return you a jquery promise which you can use later.
Now in what ever method you want to make ApiCall just use like this -
GetData(props,EmpCollection)
{
this.ApiCall(props)
.then(
function(data){
console.log(data);
// set the state here
},
function(error){
console.log(error);
}
);
}

Resources