When I try to setState after the get axios request it doesn't seem that I have the data ready to render.In console, I am getting a response but unable to access the response in the state.
import React, { Component } from 'react';
import axios from 'axios';
import { Grid, Row, Col } from 'react-flexbox-grid';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import stylefile from './stylefile';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import { withStyles } from '#material-ui/core/styles';
import '../App.css';
class TitleBar extends Component {
constructor() {
super();
this.state ={
data:[],
}
}
componentDidMount() {
axios.get('http://api.abc',
{
headers: { "Postman-Token": "abc"}
})
.then((response) => {
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
render() {
const { classes } = this.props;
console.log(this.state.data,"data response")
return (
<div>
{
this.state.data.map(((item,key) => (
<div>
//
</div>
)))}
</div>
);
}
}
export default withStyles(stylefile)(TitleBar);
console.log(error);
});
}
//console.log(this.state.data) -- is undefined
Your API response object includes an object like:
const response = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" }
],
HASH: "3--0"
}
};
So, you need response.data.DATA for your state here:
this.setState( { data: response.data.DATA } );
Here is a working example of mimicking your situation.
const remoteData = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" },
],
HASH: "3--0",
},
};
const fakeRequest = () =>
new Promise( resolve => setTimeout( () => resolve( remoteData ), 1000 ) );
class App extends React.Component {
state = {
data: [],
};
componentDidMount() {
fakeRequest().then( response => this.setState( { data: response.data.DATA } ) );
}
render() {
return (
<div>
{this.state.data.map( el => (
<div key={el.id}>
<p>{el.id}</p>
<p>{el.name}</p>
</div>
) )}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById( "root" )
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
From React official docs,
componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering
Also,one should use componentDidMount as componentWillMount is deprecated in new version of react.
componentDidMount() {
axios.get('http://api./abc',
{
headers: { "Postman-Token": "abc" }
})
.then((response) => { //use arrow to get setState on this call without any extra binding or placeholder variable
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
Try to fix those lines of code:
constructor(props) { //fixed
super(props); //fixed
this.state ={
data:[],
}
}
This is just the way ReactJS set up the constructor() method for a class component. We just obey the React's rules while working with it.
From the Official React Document, they said:
The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
For more information about the contructor() method: https://reactjs.org/docs/react-component.html#constructor
This is a working example which I've already made just for you as a reference.
The demo is now available on CodeSandBox: https://codesandbox.io/s/8xvn8yl1l2
TitleBar.js
import React, { Component } from 'react';
import axios from 'axios';
export default class TitleBar extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
console.log(res.data);
this.setState({ data: res.data });
})
.catch(err => console.log(err.message));
}
render() {
return (
<div>
{this.state.data.map(i => (
<div key={i.id}>
<h2>{i.title}</h2>
</div>
))}
</div>
);
}
}
App.js
import React, { Component } from 'react';
import TitleBar from './components/TitleBar';
class App extends Component {
render() {
return (
<div>
<TitleBar></TitleBar>
</div>
);
}
}
export default App;
By following this example, if the this.state.data is still undefine, then there are two things that we could focus on in order to successfully debug:
1.The structure of the response data object. In your case, the solution could be
this.setState({
data: response.data.DATA
})
2.Does the API work as expected.
Hopefully that helps.
Related
I have two classes, App and qrCode. The qrCode class returns a image of the qrcode based on the address provided in that classes state. The app class displays the qrcode component. In the method getSessionID, the app makes a request to the server, then should call updateQrCode to update the qrcode that is being shown.
How can I call the method updateQrCode from App.js? I can't instantiate a new QrCode, because that is not the instance that is being displayed.
App.js:
import React, { Component } from "react";
import "./App.css";
import QrCode from "../qrCode/qrCode";
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
<QrCode />
</header>
</div>
);
}
getSessionID() {
fetch("/getSessionID")
.then((res) => {
// code here to update the qr code in the instance of QrCode
// QrCode.updateQrCode("test")
})
.then((data) => console.log(data.message));
}
}
export default App;
QrCode.js:
import "./qrCode.css";
import QRCode from "qrcode";
import React, { Component } from "react";
class QrCode extends Component {
constructor(props) {
super(props);
this.state = {
qrCode: {
address: null,
image: null,
},
};
}
componentDidMount() {
generateQrCode(this.state.qrCode.address).then(
function (image) {
this.setState({
qrCode: {
address: this.state.qrCode.address,
image: image,
},
});
}.bind(this)
);
}
updateQrCode = () => {
this.setState({
qrCode: {
address: "asdf",
image: null,
},
});
};
render() {
return (
<div>
<img src={this.state.qrCode.image} alt="QR Code"></img>
</div>
);
}
}
function generateQrCode(address) {
return new Promise(function (resolve, reject) {
if (address) {
QRCode.toDataURL(address, {
errorCorrectionLevel: "h",
color: {
dark: "#000000",
light: "#0000",
},
})
.then((dataURI) => {
resolve(dataURI);
})
.catch((err) => {
console.error(err);
reject(err);
});
} else {
return address;
}
});
}
export default QrCode;
App.js is the parent component and QrCode.js is the child component. From parent to child you can pass by props directly.
In App.js, create a state object and update it on receiving fetch response. React re-renders component and its children whenever state object changes.
App.js
import React, { Component } from "react";
import "./App.css";
import QrCode from "../qrCode/qrCode";
class App extends Component {
constructor(props) {
super(props);
this.state = { qrCodeImage: null};
}
render() {
return (
<div className="App">
<header className="App-header">
<QrCode qrCodeImage={this.state.qrCodeImage} />
</header>
</div>
);
}
getSessionID() {
fetch("/getSessionID")
.then((res) => {
// code here to update the qr code in the instance of QrCode
// QrCode.updateQrCode("test")
})
.then((data) => this.setState({ qrCodeImage:data }));
}
}
export default App;
QrCode.js
updateQrCode = () => {
this.setState({
qrCode: {
address: "asdf",
image: this.props.qrCodeImage,
},
});
};
I am trying to fetch data in componentDidMount lifecycle method of react but I am not getting it.
my method is:
componentDidMount() {
const { taskId } = this.props
getTask(taskId)
.then(data => {
console.log(data);
this.setState({task: data});
})
}
my api is:
export const getTask = (unique_id) => {
console.log(unique_id)
return fetch('https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/'+ unique_id).then(res => {
return res.json();
});
};
this is my whole component:
import React, { Component } from 'react'
import { getTask } from '../../modules/clients';
import ClientTaskShow from '../../components/tasks/ClientTaskShow'
class ClientTaskShowContainer extends Component {
constructor(props) {
super(props)
this.state = {
messageModalOpen: false,
selectedPartnerId: null,
task:{}
}
}
componentDidMount() {
console.log("hello")
const { taskId } = this.props
getTask(taskId)
.then(data => {
console.log(data);
this.setState({task: data});
})
}
render() {
const taskSelected = this.state.task;
console.log(taskSelected)
return (
<ClientTaskShow
task={taskSelected}
/>
)
}
}
export default ClientTaskShowContainer;
code from where calling clienttaskShowContainer:
import React from 'react'
import Head from 'next/head'
import Layout from '../../components/Layout'
import ClientTaskShowContainer from '../../containers/tasks/ClientTaskShowContainer'
import requireAuth from '../../lib/requireAuth'
const ClientTasksShow = ({ query }) => {
const { taskId } = query
return (
<Layout fluid fullHeight clientTaskHeader='true'>
<Head>
<title>Client Task Details | Punctual</title>
</Head>
<ClientTaskShowContainer taskId={taskId} />
</Layout>
)
}
ClientTasksShow.getInitialProps = async ({ query }) => ({
query
})
export default requireAuth(ClientTasksShow)
I think its not hitting the API even. Although it hit once I restart the server but not again. I am not able to replicate the problem.
At some sites I found we should use .then for API call others says we can't pass perimeter in API call in componentDidMount. What is the exact solution for this. Please help. Thanks in advance.
This code is working
//Calling component
import React from "react";
import CallComp from "./CallComp";
import ReactDOM from "react-dom";
function App() {
return (
<div className="App">
<CallComp taskId={"7693fbf81a33"} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Child Component
import React, { Component } from "react";
import ClientTaskShow from "./ClientTaskShow";
class ClientTaskShowContainer extends Component {
constructor(props) {
super(props);
this.state = {
task: {}
};
}
componentDidMount() {
const { taskId } = this.props;
fetch(
`https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/${taskId}`
)
.then(response => response.json())
.then(data => this.setState({ task: data }))
.catch(error => console.log("the error is", error));
}
render() {
const taskSelected = this.state.task;
console.log("task selected is ", taskSelected);
return (
<div>
{Object.keys(taskSelected).length ? (
<ClientTaskShow task={taskSelected} />
) : (
<div>No data to show</div>
)}
</div>
);
}
}
export default ClientTaskShowContainer;
// Demo ClientTaskShow
import React from "react";
const ClientTaskShow = ({ task }) => {
return <h1>{task.unique_code}</h1>;
};
export default ClientTaskShow;
Actually its working
console.log(data) returns error message from api
You should return promise from function to know api request is resolved or not
try this:
export const getTask = (id) => {
return new Promise(function (resolve, reject) {
fetch('https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/' + id).then((res) => {
resolve(res.json())
})
});
}
And call like this:
componentDidMount() {
getTask(1).then((data)=>{
console.log(data);
});
}
You can replace params with your id
Hope this helps.
I am serving some content from my API.
I want display response from API in my react component.
Response is html with bundled all assets inline by webpack.
How can I do it?
I tried dangerouslySetInnerHTML but it crashes my javascript inside returned html.
My cmp :
import React, { Component } from 'react';
import axios from 'axios';
export default class Report extends Component {
constructor() {
super();
this.state = {
id: null,
report: null
};
}
getParam(param){
return new URLSearchParams(window.location.search).get(param);
}
componentWillMount() {
axios.post(`/url`,
{
'id': this.getParam('id'),
}
)
.then(res => {
this.setState({id: res.data});
setTimeout(() => {
axios.get(`https://rg.ovh/`+this.state.id)
.then(res => {
this.setState({report: res.data})
});
}, 1900);
});
}
render() {
return (
<div dangerouslySetInnerHTML={ {__html: this.state.report} } />
);
}
}
import axios from 'axios';
import React, { Component } from 'react';
import renderHTML from 'react-render-html';
class App extends Component {
constructor() {
super();
this.state = {
htmlString: ''
};
}
componentDidMount() {
axios.get('http://localhost:5000').then(response => {
this.setState({ htmlString: response.data })
}).catch(err => {
console.warn(err);
});
}
render() {
const { htmlString } = this.state;
return (
<div className="App">
{renderHTML(htmlString)}
</div>
);
}
}
export default App;
Yo guys, getting error 'contacts.map is not a function' not sure why is that ? just starting in react maybe missing something obvious. I'm getting the data when I console log all good.
code below:
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
componentDidMount(){
axios.get('url')
.then(response => {
this.setState({ contacts: response.data });
})
.catch(function (error) {
console.log(error);
})
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;
Apparently its an object not an array...
How can i render this object then?
It has one property for now but will have more later on: tried JSON.stringify(obj)
{hello: "test"}
The problem is that you set contacts to response.data, which evidently it's not an array.
componentDidMount fires after the component is mounted and tries to get the string 'url'. When state is updated, the component is redrawn and it gives the error.
Since the contacts is an object I would recommend you to do Object.keys and then .map on it so that you can get object keys and it’s values.
One more thing never forget to add unique key to the parent jsx element when you iterate array of data or an object like below.
<div>
{Object.keys(contacts).map((name, index) => (
<h1 key={'Key'+index}>{contacts[name]}</h1>
))}
</div>
From react docs:
Note:
These methods are considered legacy and you should avoid them in new code:
UNSAFE_componentWillMount()
When you want to wrap an object you can simply wrap it in brackets
class Contacts extends Component {
constructor() {
super();
this.state = {
contacts: [],
}
}
componentDidMount() {
axios.get('url')
.then(({ data }) => {
this.setState({ contacts: [data] });
})
.catch((error) => {
console.log(error);
});
}
render() {
const { contacts } = this.state;
return (
<div>
{contacts.map(contact => (
<h1 key={/* unique key */}>contact.hello</h1>
))}
</div>
);
}
}
Use async await to get the response before the component is mounted
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
async componentWillMount(){
const response = await axios.get('url')
this.setState({ contacts: response.data })
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;
I'm trying to set a state for my Axios POST response but the array where I map the data is still empty. The data fetching is working good, but I just need to move all the data with the 'subject' keywords to my Todos array and print them out.
Here is my code so far;
App.js
import React, { Component } from 'react';
import axios from 'axios';
import Todos from "./Components/Todos"
class App extends Component {
constructor(){
super();
this.state = {
todos:[]
}
}
// AXIOS request
getTodos(){
var postData = {
"startDate": "startDate",
"endDate": "endDate",
"studentGroup": ["ID"]
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic " + btoa("username" + ":" + "password")
}
};
axios.post('url', postData, axiosConfig)
.then((data) => {
console.log(data);
this.setState({todos: data.reservations ? data.reservations : []}, function(){
console.log(this.state);
})
})
.catch((err) => {
console.log("Error: ", err);
})
}
componentWillMount(){
this.getTodos();
}
componentDidMount(){
this.getTodos();
}
render() {
return (
<div className="App">
<Todos todos={this.state.todos}/>
</div>
);
}
}
export default App;
Todos.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';
class Todos extends Component {
render() {
let todoItems;
if(this.props.todos){
todoItems = this.props.todos.map(todo => {
return (
<TodoItem key={todo.subject} todo={todo} />
);
});
}
return (
<div className="Todos">
<h3>Results:</h3>
{todoItems}
</div>
);
}
}
export default Todos;
TodoItem.js
import React, { Component } from 'react';
class TodoItem extends Component {
render() {
return (
<li className="Todo">
<strong>{this.props.todo.subject}</strong>
</li>
);
}
}
export default TodoItem;
Snippet from console before setState:
Should I use some other function instead of an arrow function?
if you don't get this.setState is undefined error then it's a bit strange. Could you fix/copy the code below and verify if that helps:
import React, { Component } from 'react';
import axios from 'axios';
import Todos from "./Components/Todos"
class App extends Component {
constructor(props){
super(props); // pass props to "father" constructor
this.state = {
todos:[]
}
this.getTodos = this.getTodos.bind(this);
}
// AXIOS request
getTodos(){
var postData = {
"startDate": "startDate",
"endDate": "endDate",
"studentGroup": ["ID"]
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic " + btoa("username" + ":" + "password")
}
};
axios.post('url', postData, axiosConfig)
.then((response) => {
if(response.data.reservations) {
this.setState({todos: response.data.reservations})
}
})
.catch((err) => {
console.log("Error: ", err);
})
}
componentDidMount(){
this.getTodos();
}
render() {
console.log(this.state.todos);
return (
<div className="App">
<Todos todos={this.state.todos}/>
</div>
);
}
}
export default App;
Now observe if console.log(this.state.todos); is called after the request is finished. If so, verify it's an empty array.
Create self reference in function
getTodos(){
var self = this; // create a this reference
var postData = { .. };
let axiosConfig = {... };
axios.post(..)
.then((data) => {
self.setState('your updated state')
})
.catch((err) => {});
}
I was having hard time with the "this" undefined. I solved it like this (calling function should be arrow function):
handleGenerateCharge =(event) => {
const header = new Headers();
header.append('Access-Control-Allow-Origin', '*');
var charge = {
name: "ABC",
createDate: Moment(Date.now()).format()
}
axios.post("http://url", charge, header)
.then((res) => {
if (res.status === 200) {
this.update();
}
}).catch((error) => {
this.setState({ errorMessage: error });
})
event.preventDefault();
}