use axios with react to get data from api - javascript

I am trying to use axios to get data from the api (https://reqres.in/) and display in my react app. Before this I fetched the data from the API using fetch method in javascript. Now I have tried coding this from various resources. How should I do it. Is it the correct method?
My app.js file-
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.successShow = this.successShow.bind(this);
this.errorShow = this.errorShow.bind(this);
}
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(function (response) {
this.successShow(response);
})
.catch(function (error) {
this.errorShow(error);
});
}
successShow(response) {
this.member = <pre>{JSON.stringify(response.data, null, '\t')}</pre> ;
}
errorShow(error) {
this.member = <pre>{JSON.stringify(error.response.data, null, '\t')}</pre>;
}
render() {
return (
<div>
<h2>Welcome to React</h2>
<h3>{JSON.stringify(this.state.person.data)}</h3>
<div>{this.member}</div>
</div>
);
}
}
export default App;
It also gives the error - Unhandled Rejection (TypeError): Cannot read property 'errorShow' of undefined.

Changes:
1. You need to bind this with then and catch callback methods, use arrow functions.
2. You didn't define the initial state and using this.state.person.data it will throw error.
3. Storing the UI in state or global variable is not a good idea, ui part should be inside render method only.
Write it like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
person: {}
}
//this.successShow = this.successShow.bind(this);
//this.errorShow = this.errorShow.bind(this);
}
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch((error) => {
this.successShow(error);
});
}
successShow(response) {
this.setState({
person: response.data
});
}
render() {
return (
<div>
<h2>Welcome to React</h2>
<h3>{JSON.stringify(this.state.person.data)}</h3>
<pre>{JSON.stringify(this.state.person.data)}</pre>
<div>{this.member}</div>
</div>
);
}
}

When you call this.errorShow() inside of the function, this is not your component object, but context of function. You should use arrow functions instead, arrow functions do not create its own this so you can access your component this:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch(error) => {
this.errorShow(error);
});
}
More info about arrow functions

Try this:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch((error) => {
this.errorShow(error);
});
}
Use arrow functions to remain the right scope of this

The problem is that the this in your then and catch callbacks doesn't refer to your class, but to the default (global) scope. You need to bind the right this. You actually already have the appropriate functions set up with this binding, so you can just use them directly:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(this.successShow)
.catch(this.errorShow);
}
In general, you can also use => function syntax, which inherits the 'this' from the scope the function is declared in, rather than using the global scope. E.g.
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(success => this.successShow(success))
.catch(error => this.errorShow(error));
}
(note the => functions are completely unnecessary here of course).
You have an additional problem, which is the you need to store member in component state (this.state.member), not just as a field, and use the setState function to update it. Otherwise your component won't re-render when you update member.

Related

React setState inside componentDidUpdate causing infinite loop

Can someone help me solve how do I setState inside componentDidUpdate and not have an infinite loop? Some suggestions said to have a conditional statement, but I am not too familiar with how do I set the conditional for my code.
This is what my code looks like:
I have a dashboard component that gets all the companies and projects data from external functions where the fetch happens and then updates the state. The projects are associated with the company's id.
I am able to get the list of all the projects in JSON, but I can't figure out how to update my projects state inside componentDidUpdate once rendered.
CompanyDashboard.js
import { getCompanys } from "../../actions/companyActions";
import { getProjects } from "../../actions/projectActions";
class CompanyDashboard extends Component {
constructor(props) {
super(props);
this.state = {
companies: [],
projects: []
};
}
componentWillMount() {
// get all companies and update state
getCompanys().then(companies => this.setState({ companies }));
}
componentDidUpdate(prevState) {
this.setState({ projects: this.state.projects });
}
render() {
const { companies, projects } = this.state;
{
companies.map(company => {
// get all the projects
return getProjects(company);
});
}
return <div />;
}
}
export default CompanyDashboard;
companyActions.js
import { getUser, getUserToken } from './cognitoActions';
import config from '../../config';
export function getCompanys() {
let url = config.base_url + '/companys';
return fetch(url, {
method: 'GET',
headers: {'token': getUserToken() }
})
.then(res => res.json())
.then(data => { return data })
.catch(err => console.log(err));
}
projectActions.js
import { getUserToken } from './cognitoActions';
import config from '../../config';
export function getProjects(company) {
let url = config.base_url + `/companys/${company._id['$oid']}/projects`;
return fetch(url, {
method: 'GET',
headers: {'token': getUserToken() }
})
.then(res => res.json())
.then(data => { return data })
.catch(err => console.log(err));
}
The following code is not doing anything meaningful. You are setting your state.projects to be equal to your state.projects.
componentDidUpdate() {
this.setState({ projects: this.state.projects })
}
Also, the following code is not doing anything meaningful because you are not saving the result of companies.map anywhere.
{
companies.map((company) => {
return getProjects(company)
})
}
It's hard to tell what you think your code is doing, but my guess is that you think that simply calling "companies.map(....) " inside your render function is going to TRIGGER the componentDidUpdate function. That is not how render works, you should go back to the drawing board on that one. It also looks like you think that using the curly brackets {} inside your render function will display the objects inside your curly brackets. That's also not true, you need to use those curly brackets inside the components. For instance: {projects}
If I had to guess... the following code is how you actually want to write your component
import { getCompanys } from '../../actions/companyActions';
import { getProjects } from '../../actions/projectActions';
class CompanyDashboard extends Component {
constructor(props) {
super(props);
this.state = {
companies: [],
projects: []
}
}
componentWillMount() {
getCompanys().then(companies => {
const projectPromises = companies.map((company) => {
return getProjects(company)
});
Promise.all(projectPromises).then(projects => {
//possibly a flatten operator on projects would go here.
this.setState({ companies, projects });
});
/*
* Alternatively, you could update the state after each project call is returned, and you wouldn't need Promise.all, sometimes redux can be weird about array mutation in the state, so look into forceUpdate if it isn't rerendering with this approach:
* const projectPromises = companies.map((company) => {
* return getProjects(company).then(project => this.setState({projects: this.state.projects.concat(project)}));
* });
*/
)
}
render() {
const { companies, projects } = this.state;
//Not sure how you want to display companies and projects, but you would
// build the display components, below.
return(
<div>
{projects}
</div>
)
}
}
export default CompanyDashboard;
componentDidUpdate has this signature, componentDidUpdate(prevProps, prevState, snapshot)
This means that every time the method gets called you have access to your prevState which you can use to compare to the new data, and then based on that decide if you should update again. As an example it can look something like this.
componentDidUpdate(prevProps, prevState) {
if (!prevState.length){
this.setState({ projects: this.state.projects })
}
}
Of course this is only an example since I don't know your requirements, but this should give you an idea.
When componentDidUpdate() is called, two arguments are passed:
prevProps and prevState. This is the inverse of
componentWillUpdate(). The passed values are what the values were,
and this.props and this.state are the current values.
`componentDidUpdate(prevProps) {
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}`
You must check the state/props if new state/props different from previous one then you can allow to update your component.
You may call setState() immediately in componentDidUpdate() but
note that it must be wrapped in a condition like in the example above,
or you’ll cause an infinite loop. It would also cause an extra
re-rendering which, while not visible to the user, can affect the
component performance. If you’re trying to “mirror” some state to a
prop coming from above, consider using the prop directly instead.
This is because componentDidUpdate is called just after a component takes up somechanges in the state. so when you change state in that method only then it will move to and from from that method and state change process

Understanding the use of this inside a class method

I am having tough time understanding use of this keyword in Javascript.
The other questions on stackoverflow I stumbled upon have been more about calling a method or function using this keyword. Like using bind or ES6 arrow function and so on..
So I have this stateful component in React and we are using Axios to intercept request
import React, { Component } from 'react';
import Modal from '../../components/UI/Modal/Modal';
import Aux from '../Aux/Aux';
const withErrorHandler = ( WrappedComponent, axios ) => {
return class extends Component {
state = {
error: null
}
componentWillMount () {
this.reqInterceptor = axios.interceptors.request.use(req => {
this.setState({error: null});
return req;
});
this.resInterceptor = axios.interceptors.response.use(res => res, error => {
this.setState({error: error});
});
}
componentWillUnmount() {
axios.interceptors.request.eject(this.reqInterceptor);
axios.interceptors.response.eject(this.resInterceptor);
}
render () {
return (
<Aux>
<Modal
//Something
</Modal>
<WrappedComponent {...this.props} />
</Aux>
);
}
}
}
export default withErrorHandler;
Something like above code, Here in above code we call interceptors which we want to remove when we want componentWillMount (to avoid memory leaks)
For that the instructor did something like this in componentDidMount followed by
this.reqInterceptor = axios.interceptors.request.use(req => {
this.setState({error: null});
return req;
this in componentWillUnmount
axios.interceptors.request.eject(this.reqInterceptor);
[Question] Can some explain me this.reqInterceptor here? like shouldn't we create a constructor and declare it there and then use it (maybe I am thinking it wrong)?
To answer your question we need a good understanding of structure of React.Component first.
React stateful components are well design to leverage a bit of object-oriented programming (though you may achieve the same pattern in other paradigms.) You have this which refers to the whole component class at your disposal. You can retrieve or assign values to properties or call bounded methods to the component by referring to this within the scope.
In stateful components React executes componentDidMount() when the DOM is ready and mounted then according to your code you assign a value to reqInterceptor property of the component by this.reqInterceptor = value..., this is basically the component that we are returning from our function function withErrorHandler { return class extends Component {...} }.
This is a common pattern to dynamically create components on fly. We can apply same in the following example to demonstrate how this works in the scope of ES6 classes:
class Service {
constructor(x) {
this.x = x;
}
}
function getMyService(extra) {
return class extends Service {
getExtra() {
return extra; // extra value like WrappedComponent or axios
}
getX() {
return this.x;
}
};
}
// result
const MyService = getMyService('some extra value'); // Returns the class
const myServiceInstance = new MyService(1); // This is what React does to instantiate your component
console.log(myServiceInstance.getX()); // -> 1
console.log(myServiceInstance.getExtra()); // -> 'some extra value'
Update:
I updated the above example to be semantically close to React.Component
The constructor will be called with the new keyword, so since the method definition is not in the constructor, you could instantiate multiple objects and you won't register every time a listener.
In this case, he wants to tie the class method to the react lifecycle (componentWillMount and componentWillUnmount).

Functions inside componentDidMount are undefined

I have the following block of code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
avatar: '',
...some more data...
}
this.fetchUser = this.fetchUser.bind(this);
}
render() {
return (
<div>
<SearchBox onSubmit={this.fetchUser}/>
<Card data={this.state} />
<BaseMap center={this.state.address}/>
</div>
);
}
componentDidMount() {
function fetchUser(username) {
let url = `https://api.github.com/users/${username}`;
this.fetchApi(url);
};
function fetchApi(url) {
fetch(url)
.then((res) => res.json())
.... some data that is being fetched ....
});
};
let url = `https://api.github.com/users/${this.state.username}`;
}
}
export default App;
However, I get a TypeError: Cannot read property 'bind' of undefined for the following line: this.fetchUser = this.fetchUser.bind(this); in the constructor where I bind the function to this.
How can I make the function, which is inside componentDidMount method, visible and available for binding?
EDIT:
The reason I'm putting the functions inside componentDidMount was because of a suggestion from another user on StackOverflow. He suggested that:
#BirdMars that because you don't really fetch the data in the parent,
and the state doesn't really hold the address object. call the fetch in
componentDidMount of the parent and update the state there. this will
trigger a second render that will pass in the new state with the
address object (the first render will be with an empty state.address
of course until you finish the fetch and update the state)
There is some fundamental misunderstanding here, you can still call the functions inside componentDidMount, however you should not define them inside it.
Simply define the functions outside componentDidMount and this will solve your problems, here is a short example
componentDidMount() {
this.fetchUser(this.state.username);
}
function fetchUser(username) {
let url = `https://api.github.com/users/${username}`;
this.fetchApi(url);
};
function fetchApi(url) {
fetch(url)
.then((res) => res.json())
.... some data that is being fetched ....
});
};
Its a simple matter of scope:
function outer(){
function inner(){
}
}
inner(); // error does not exist
As birdmars suggested you should call this.fetchUser() inside component did mount. but declare the function outside!
class App extends Component {
render() {
return (
<div>
<SearchBox onSubmit={this.fetchUser}/>
<Card data={this.state} />
<BaseMap center={this.state.address}/>
</div>
);
}
fetchUser(username) {
let url = `https://api.github.com/users/${username}`;
this.fetchApi(url);
};
fetchApi(url) {
fetch(url)
.then((res) => res.json())
.... some data that is being fetched ....
});
};
componentDidMount() {
let url = username; //frome somewhere, most probably props then use props Changed function instead!
var user = his.fetchUser(url)
this.setState(() => {user: user});
}
}
export default App;
If you declare the functions inside componentDidMount they are only visible in that scope and get be accessed by the component itself. Declare them in your component.
What he was talking about in this post was to call the functions from componentDidMount, but not to declare them in there.
"Another guy from StackOverflow" suggest you to call functions in componentDidMount() method, but not to define them there.
So you should do this instead
class App extends Component {
constructor(props) {
...
this.fetchUser = this.fetchUser.bind(this);
this.fetchApi= this.fetchApi.bind(this);
}
...
fetchUser(username) {...}
fetchApi(url) {...}
componentDidMount() {
this.fetchUser(...);
...
}
}

Trivial issue with React setState function

I am actually at a loss to figure out why this isn't working as I have spent a lot more hours than usual on how to get it fixed. The problem is I am using axios to make a REST call to get the data to be rendered. Inside the block to handle the response, even though I am able to retrieve the data the 'this' object somehow fails to refer to the correct object and I get an error. I dono why this is happening but any help on it will be highly appreciated.
Posting my code snippet below. I have tried saving the context of this outside the axios call scope and used the new variable but that too does not help. Here is the error I get in my console
TypeError: _this2.setState is not a function
import React, {Component} from 'react';
import axios from 'axios';
import './RouteList.css';
class RouteList extends Component{
constructor(){
super();
this.setState = {
allRoutes: {},
selectedRoutes: {}
};
}
componentDidMount(){
const that = this;
//Retrieve the SF-Muni route list
axios.get('http://webservices.nextbus.com/service/publicJSONFeed?command=routeList&a=sf-muni')
.then(response => {
console.log(response);
that.setState({ allRoutes: response.data.routes });
})
.catch((error) => {
console.log(error);
});
}
render(){
return (
<div className="transit-routes">
{/*TODO-Code to render data.*/}
</div>
);
}
}
export default RouteList;`
The problem is that you are overwriting the setState method in the constructor, try to set the initial state like this:
this.state = {
allRoutes: {},
selectedRoutes: {}
};
Also, when using an arrow function, there's no need to save the parent scope, the function will run under the same scope as the outer function.

what to do with response object in react js

i'm working with react to complete the front end of a rest application.
I have json being sent to the front end, and I use fetch .
fetch('/task')
.then(function(data) {
return data.json();
})
.then(function(json) {
json.tasks.forEach(function(task) {
console.log(task.name)
})
});
So i'm able to console.log each task.name, but where to now? How do I get my component to display each task as a ?
Basically, where in a component does this type of logic go? Do i save the fetch request to a variable and then setState = variable?
this is my component:
class Task extends React.Component {
render() {
return <p> hey </p>
}
}
You need to initialize a state object, which you can update when the fetch is complete:
class Task extends React.Component {
constructor () {
super()
this.state {
tasks: null
}
}
componentDidMount () {
fetch('/task')
.then((data) => {
return data.json()
})
.then((json) => {
this.setState({ tasks: json.tasks })
})
}
renderTaskList () {
if (this.state.tasks) {
return (
<ul>
{this.state.tasks.map((task, i) => <li key={i}>{task.name}</li>)}
</ul>
)
}
return <p>Loading tasks...</p>
}
render () {
return (
<div>
<h1>Tasks</h1>
{this.renderTaskList()}
</div>
)
}
}
Edit: Re-reading this answer, I just wanted to note that it is not necessary to initialize the tasks property of the state object in this case. You could also just do something like:
this.state = {}
However, I think there is some value in explicitly naming the various properties of your state object, even if they are initialized as null. This allows you to write components whose state is documented in the constructor, and will prevent you or your teammates from later guessing how a component's state is modeled.

Categories