React : how to properly use componentDidMount to make an api call - reactjs

My react app doesnt show my api calls. At least some of them.
I would like to make sure my code does wait for the api call to resolve.
I saw a tutorial here https://codewithnico.com/react-wait-axios-to-render/ where they use a functional component with if isLoading to show loading status and useEffect to make the api call and wait for the results.
I have a class component and I would like to check if the use of componentDidMount is indeed waiting for the api call to resolve like useEffect is in the functional tutorial i saw
Here is the code:;
import React from "react";
import axios from "axios";
import { StreamField } from "./StreamField/StreamField";
class PostDetail extends React.Component {
constructor(props) {
super(props); this.state = {
post: [],
loading: true, };
}
componentDidMount() {
const pk = this.props.match.params.id;
axios.get(`/api/cms/pages/${pk}/`).then((res) => {
const post = res.data;
this.setState({
post,
loading: false });
}) }
render() {
if (!this.state.loading) {
const post = this.state.post;
return (
<div className="col-md-8">
<img src={post.header_image_url.url} className="img-fluid rounded" alt=""/>
<hr />
<h1>{post.title}</h1>
<hr />
<StreamField value={post.body} />
</div> );
}
else {
return <div className="col-md-8">Loading...</div>;
}
}
}
export { PostDetail };
What do you think? Is my code in cause for my component not loading properly ( the delay to resolve is quite long on my test machine : several seconds)

Related

React - what are the steps to get data from api and render it?

I am building a site just like stackoverflow.com. I want my home page to display top questions. For that, I have sample questions on the backed. Now, I want to display only the question and tags from the questions array.
The code is in the image
I have made axios connection for that:
const instance = axios.create({
baseURL: "https://2w2knta9ag.execute-api.ap-south-1.amazonaws.com/dev", });
instance.defaults.headers.post["Content-Type"] = "application/json";
To connect it, I wrote the command: instance.get("/questions)
Now, how do I display only the question and tags??
EDIT:
On using the code given bellow, my js file now becomes:
import React from 'react';
import instance from '../../api';
class QuestionList extends React {
componentDidMount() {
instance
.get("/questions")
.then((res) => {
this.setState({ data: res.data });
});
}
render () {
const { data } = this.state;
return <div>
{
data && data.map(d => {
return <div>question: {d.question}, tags: {d.tags}</div>;
})
}
</div>
}
}
export default QuestionList;
But, this is just making my site in a loading state, and it gets hanged!!
If I understood correctly, you want to get an array only with the tags and the question. if so, you can use Array.prototype.map for this
const questions = result.map(({ question, tags }) => ({ question, tags }))
First you export the axios instance so that it can be used from other components.
Now you can send the api request in componentDidMount and update your component's state with the data.
And in render function, you just get the value from state and display.
If you are new to react, learn React Hooks and know that componentDidMount method is the best place to send api requests.
For Example:
import React from 'react';
import instance from '../../api';
class QuestionList extends React.Component {
constructor() {
super();
this.state = {
data: [],
};
}
componentDidMount() {
instance.get('/questions').then((res) => {
this.setState({ data: res.data });
});
}
render() {
const { data } = this.state;
return (
<div>
{data &&
data.map((d) => {
return (
<div>
question: {d.question}, tags: {d.tags}
</div>
);
})}
</div>
);
}
}
export default QuestionList;

componentDidMount does not fire on second time then I pass second router paramater value

I am handling react component by using router parameter,and also I have set router paramater value in state using componentDidMount event life cycle.It works fine as per my requirement,but "componentDidMount" does not fire on second time when I pass second router paramater value.Please check my below code and advise how to do this.
import React, { Component } from 'react';
import ConfigItem from '../../Config';
import axios from 'axios';
class ZohoDashboard extends Component {
constructor(props) {
super(props);
this.state = {url: ''};
}
componentDidMount() {
console.log('mount');
axios.get(ConfigItem[0].APIPath+'Menus/'+this.props.match.params.id)
.then(res => {
console.log(res.data.data[0].URL);
this.setState({url:res.data.data[0].URL});
})
}
render() {
console.log('render');
return (
<div class="embed-responsive embed-responsive-21by9">
<iframe class="embed-responsive-item" src={this.state.url} allowfullscreen></iframe>
</div>
);
}
}
export default ZohoDashboard;
Router params in url are supposed to trigger a prop change and hence do not remount the component. Instead they trigger a re-render, you can make use of componentDidUpdate to trigger an API when route param changes
class ZohoDashboard extends Component {
constructor(props) {
super(props);
this.state = {url: ''};
}
componentDidMount() {
console.log('mount');
this.fetchData();
}
fetchData = () => {
axios.get(ConfigItem[0].APIPath+'Menus/'+this.props.match.params.id)
.then(res => {
console.log(res.data.data[0].URL);
this.setState({url:res.data.data[0].URL});
})
}
componentDidUpdate(prevProps) {
if(prevProps.match.params.id !== this.props.match.params.id) {
this.fetchData()
}
}
render() {
console.log('render');
return (
<div class="embed-responsive embed-responsive-21by9">
<iframe class="embed-responsive-item" src={this.state.url} allowfullscreen></iframe>
</div>
);
}
}

Using ReactJs to fetch data from an API but getting completely blank page with no errors

Guys Kindly i need your help. I am trying to fetch data from an Api and display it in the dom. I can see the data in the console but when i try to return data it shows a blank page and no errors. Below is my code.
App.js file
import React from "react";
import "./App.css";
import Movieapp from "./Movieapp";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
title: [],
date: [],
image: []
};
}
componentDidMount() {
fetch(`https://yts.mx/api/v2/list_movies.json?quality=3D`)
.then(res => res.json())
.then(data => {
console.log(data.data);
this.setState = {
title: data.data.movies[0].title,
date: data.data.movies[0].date_uploaded,
image: data.data.movies[0].background_image
};
});
}
render() {
return (
<div className="App">
<Movieapp
title={this.state.title}
date={this.state.date}
image={this.state.image}
/>
</div>
);
}
}
export default App;
Movieapp.js file
import React from "react";
const Movieapp = props => {
return (
<div>
<h1>{props.title}</h1>
<h1>{props.date}</h1>
<div>{props.image}</div>
</div>
);
};
export default Movieapp;
this.setState is a function, not a property. You have to use it properly:
this.setState({
title: data.data.movies[0].title,
date: data.data.movies[0].date_uploaded,
image: data.data.movies[0].background_image
});
Also, even though I guess you are just trying things our, there are few things to be aware of:
movies[0] can be undefined
You are getting multiple movies but showing only one. It's probably better to just save the whole data array in the state and iterate over the results in the render method

React componentDidMount does not show axios response

I have React component RandomQouteMachine which is supposed to get response from the provided URL and display it on the page. I don't see response displayed. The debug 'Did component mount ?' message is missing too..
import React, { Component } from 'react';
import axios from 'axios';
lass RandomQouteMachine extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}
componenetDidMount() {
console.log('Did component mount ?');
axios.get('https://api.icndb.com/jokes/random')
.then((res) => {
this.setState({data:res.value.joke});
})
}
render() {
return (
<div>
Here it is:
{this.state.data}
</div>
);
}
}
export default RandomQouteMachine;
Am I using componenetDidMount() correctly ? I can see only 'Here it is:' displayed on page
check your spelling.
componenetDidMount !== componentDidMount

componentWillMount does not finish before render

I am facing some difficulties regarding to state in reactjs.
As far as I know componentWillMount is a place to load the data with ajax call before the components get rendered.
I have a simple simple project which populates stack of panels with a loaded data and show it on board. However the data from ajax call do not get set before rendering of the component and this leads to rendering of the board with an empty array. The follwoing is my complete source:
import React from "react";
export class Panel extends React.Component{
render() {
return (
<div>
<div className="row panel">
<div className="col-sm-12 header">sfsfsf</div>
<div className="col-sm-12 body">fsdfsfs</div>
<div className="col-sm-12 footer">fasfaf</div>
</div>
</div>
);
}
}
and Board class which is a root of the problem is as follows :
import React from "react";
import {Panel} from "./Panel";
export class Board extends React.Component{
constructor(props){
super();
this.state={news: []};
}
componentWillMount(){
this.state={news: []};
$.ajax({
url: "http://localhost:3003/json.txt",
dataType: 'json',
cache: false,
success: function(data) {
var arr=[];
for (var key in data) {
arr.push(data[key]);
console.log(data[key]);
}
this.state={news: arr};
}});
}
render() {
return (
<div>
{
this.state.news.map((item,i)=> <Panel key="i"/>)
}
</div>
);
}
}
Also the last class is index.js:
import React from "react";
import {render} from "react-dom";
import {Board} from "./component/Board";
class App extends React.Component{
render(){
return(
<div>
<Board/>
</div>
);
}
}
render(<App/>, document.getElementById('middle'));
So as you can see in the Board.js class I initialize my array in render function and then I use componentWillMount to fill the news array which I expect it to happen after componentWillMount is finished but in my case the array is empty when rendering happens. Any idea?
*********UPDATE***************
I also tried it with componentDidMount but it did not work and the same problem
componentWillMount() is finishing before render but because ajax is async it will not execute until the request completes.
You should not set state using this.state = .... Instead use:
this.setState({news: arr});
This will set the value and trigger the component and all children to render. Just write your render function to handle null data nicely and you'll have your expected result without blocking JS execution.
As suggested here it is with proper binding:
import React from "react";
import {Panel} from "./Panel";
export class Board extends React.Component{
constructor(props){
super();
}
getInitialState() {
return {news: []};
}
componentWillMount(){
$.ajax({
url: "http://localhost:3003/json.txt",
dataType: 'json',
cache: false,
success: (data) => {
var arr=[];
for (var key in data) {
arr.push(data[key]);
console.log(data[key]);
}
this.setState({news: arr});
}
});
}
render() {
return (
<div>
{
this.state.news.map((item,i)=> <Panel key="i"/>)
}
</div>
);
}
}
The arrow function handles the binding. Simmilar to function(){}.bind(this)
You are doing a ajax call which is async... By definition it will continue to execute the code without waiting for the ajax's response.
you can turn that synchronous by setting async: false on the $.ajax options.

Resources