Axios result undefined inside render() method in React - reactjs

I'm trying to do axios.get() request inside my react component:
I'm fetching this data from my mongo database and nodejs server is running on localhost:9000:
This is my code:
import React, { Component } from 'react'
import axios from 'axios';
export default class Home extends Component {
state = {
data : []
}
async componentDidMount() {
const {data} = await axios.get("http://localhost:9000/data")
this.setState({data});
console.log(this.state.data)
}
render(){
console.log(this.state.data);
return (
<div>
{this.state.data[0]['title']}
</div>
);
}
}
Problem is that {this.state.data[0]['title']} says
cannot read property 'title' of undefined
How can I correct this?
Thanks in advance

try it:
axios.get('http://localhost:9000/data')
.then(function (response) {
// handle success
this.setState({data:response.data});
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
and I think you must be used this like:
state = {
data : [{
_id:"",
what:"",
title:"",
__v:0
}]
}
{this.state.data[0].title}

Related

React Toastify - Not getting error notification

What I want is, when I get no data from the api, instead of this No data, I want A notification or toast.error to get displayed.
shops.jsx
import React from 'react';
import './shops.css';
import Shop from './shop'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
require('dotenv').config()
const TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1ZjFiMjNlYTQxNmJhMjQ3YjQ5MDk4Y2IiLCJlbWFpbCI6Img1aW1icjQ0NGQ7QHR5cC5pbiIsImlhdCI6MTU5NjgxMTU5MSwiZXhwIjoxNTk2ODE1MTkxfQ.UyrUkbNWzenf50FL8AZE1iZaii11P7MwdXpKmoCB9nM";
class Shops extends React.Component {
constructor(props) {
super(props);
this.state = {
shops: []
};
}
componentDidMount() {
console.log(process.env.REACT_APP_BaseURL);
// replace with correct URL: http://localhost:5000/api/shops/allShops
fetch(process.env.REACT_APP_BaseURL, {
method: "get",
headers: new Headers({
Authorization: `Bearer ${TOKEN}`
})
})
.then(response =>response.json())
.then(data => {
this.setState({ shops: data.fetchedShops });
toast.success("API LOADED SUCCESSFULLY","SUCCESS");
})
.catch(err =>{
console.log("Error", err);
if(err){
toast.error("error occured");
}
});
}
render() {
const shops =
this.state.shops.length > 0 ?
this.state.shops.map(item => (
<Shop name={item.shopname} address={item.address} mobile={item.phoneNumber} />
))
: <span >No data</span>;
console.log(this.state.shops);
return <div id="container">{shops}</div>;
}
}
export default Shops;
In the 6th line you can see <span >No data</span> instead of this I want a toast.error notification, but when I write toast.error("No data"); instead of this span i got something like this instead of error notification
If you want to toast that there is no data when the array is empty it needs to be done in two steps since render is a pure function, i.e. without side effects
Issue toast side-effect in component lifecycle functions, i.e. componentDidMount and/or componentDidUpdate
Render null when toasting no data, or since the map can handle empty arrays without issue, just return the empty map result array
Code
class Shops extends Component {
state = {
shops: []
};
checkShops = () => {
const { shops } = this.state;
if (!shops.length) {
toast.error("No Data");
}
};
componentDidMount() {
this.checkShops(); // not really needed if fetch for data first
}
componentDidUpdate() {
this.checkShops();
}
render() {
const { shops } = this.state;
return (
<div id="container">
{shops.map((item) => <div>Data</div>)}
</div>
);
}
}

How to setState after getting data from Firestore

I am currently able to get user data from the Firestore however I'm having trouble saving the users document data. I'm getting an error below in my console
TypeError: this.setState is not a function
at Object.next (RepRequest.js:32)
at index.cjs.js:1344
at index.cjs.js:1464
I attempted to follow another user's question from
Can't setState Firestore data, however still no success.
I do have a two api request right after getting the data and I am able to setState then. I tried incorporating the Firestore request in the promise.all but was unable to successfully, which is why I have it separated. Maybe I'm headed down the wrong path, any guidance is appreciated.
import React, { useEffect, useState } from "react";
import app from "./config/base.js";
import axios from "axios";
export default class RepRequest extends React.Component {
constructor(props) {
super(props);
this.state = {
userInfo: [],
fedSens: [],
fedReps: []
};
}
componentDidMount() {
const items = [];
app.auth().onAuthStateChanged(function(user) {
if (user) {
console.log("User is signed in");
let db = app
.firestore()
.collection("user")
.doc(user.uid);
db.get().then(doc => {
if (doc.exists) {
console.log("Document data:", doc.data());
items.push(doc.data());
} else {
console.log("No doc exists");
}
});
}
this.setState({ userInfo: items });
});
Promise.all([
axios.get(
`https://api.propublica.org/congress/v1/116/senate/members.json`,
{
headers: { "X-API-Key": "9wGKmWl3kNiiSqesJf74uGl0PtStbcP2mEzSvjxv" }
}
),
axios.get(
`https://api.propublica.org/congress/v1/116/house/members.json`,
{
headers: { "X-API-Key": "9wGKmWl3kNiiSqesJf74uGl0PtStbcP2mEzSvjxv" }
}
)
]).then(([rest1, rest2]) => {
this.setState({
fedSens: rest1,
fedReps: rest2
});
});
}
render() {
if (this.state.fedReps.length <= 0)
return (
<div>
<span>Loading...</span>
</div>
);
else {
console.log(this.state.fedReps);
return <div>test</div>;
}
}
}
Your problem arises from mixing lambda function declarations ((...) => { ... }) and traditional function declarations (function (...) { }).
A lambda function will inherit this from where it was defined but a traditional function's this will be isolated from the context of where it was defined. This is why it is common to see var self = this; in legacy-compatible code because this usually didn't match what you wanted it to.
Here is an example snippet demonstrating this behaviour:
function doSomething() {
var anon = function () {
console.log(this); // 'this' is independent of doSomething()
}
var lambda = () => {
console.log(this); // inherits doSomething's 'this'
}
lambda(); // logs the string "hello"
anon(); // logs the 'window' object
}
doSomething.call('hello')
Solution
So you have two approaches available. Use whichever you are comfortable with.
Option 1: Use a lambda expression
app.auth().onAuthStateChanged(function(user) {
to
app.auth().onAuthStateChanged((user) => {
Option 2: Assign a "self" variable
const items = [];
app.auth().onAuthStateChanged(function(user) {
// ...
this.setState({ userInfo: items });
}
to
const items = [];
const component = this; // using "component" makes more sense than "self" in this context
app.auth().onAuthStateChanged(function(user) {
// ...
component.setState({ userInfo: items });
}

React doesn't render data coming from an api response

I've seen a lot of questions and I couldn't get the solution
here is my code:
import React, { Component } from "react";
import axios from "axios";
import "./tree.css";
import "./mainTree";
class TablesTree extends Component {
constructor(props) {
super(props);
this.data = this.props.info;
this.state = {
fields: [],
data: [],
show: false
};
}
componentDidMount() {
var dataGet = [];
this.props.tables.forEach((name, i) => {
this.getFieldsTable(name.TABLE_NAME, (err, res) => {
if (res) {
dataGet.push({
TABLE_NAME: name.TABLE_NAME,
columns: res
});
}
});
});
this.setState({ data: dataGet });
}
getFieldsTable(table, callback) {
axios
.get(`table/columns?name=${this.data.user}&psw=${this.data.password}&schema=${this.data.schema}&table=${table}`)
.then(response => {
callback(null, response.data);
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<div>
{this.state.data
? this.state.data.map((itm, i) => {
return (
<div>
<h1>{itm.TABLE_NAME}</h1>
</div>
);
})
: null}
</div>
);
}
}
export default TablesTree;
I've made console.log of the this.state.data
and the data is in there, but it doesn't renders anything
I've tried a lot of soutions, but I still without rendering the data, I will apreciate your help.
There's a few things I would change about your code, but most importantly you need to do this.setState after your push to dataGet (inside of your callback function).
Because your API call is asynchronous, you are only calling setState once when your component is initially mounted (and while dataGet is still empty).
getFieldsTable is asynchronous, so the dataGet array will be empty when you call setState.
You could return the promise from getFieldsTable and use Promise.all on all the promises, and use the data when all of them have resolved.
Example
class TablesTree extends Component {
// ...
componentDidMount() {
const promises = this.props.tables.map(name => {
return this.getFieldsTable(name.TABLE_NAME).then(res => {
return {
TABLE_NAME: name.TABLE_NAME,
columns: res
};
});
});
Promise.all(promises).then(data => {
this.setState({ data });
});
}
getFieldsTable(table) {
return axios
.get(`table/columns?name=${this.data.user}&psw=${this.data.password}&schema=${this.data.schema}&table=${table}`)
.then(response => {
return response.data;
})
.catch(error => {
console.log(error);
});
}
// ...
}

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
});

Handling Axios error in React

I have a React component that calls a function getAllPeople:
componentDidMount() {
getAllPeople().then(response => {
this.setState(() => ({ people: response.data }));
});
}
getAllPeople is in my api module:
export function getAllPeople() {
return axios
.get("/api/getAllPeople")
.then(response => {
return response.data;
})
.catch(error => {
return error;
});
}
I think this is a very basic question, but assuming I want to handle the error in my root component (in my componentDidMount method), not in the api function, how does this root component know whether or not I the axios call returns an error? I.e. what is the best way to handle errors coming from an axios promise?
Better way to handle API error with Promise catch method*.
axios.get(people)
.then((response) => {
// Success
})
.catch((error) => {
// Error
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// console.log(error.response.data);
// console.log(error.response.status);
// console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the
// browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
The getAllPeople function already returns the data or error message from your axios call. So, in componentDidMount, you need to check the return value of your call to getAllPeople to decide whether it was an error or valid data that was returned.
componentDidMount() {
getAllPeople().then(response => {
if(response!=error) //error is the error object you can get from the axios call
this.setState(() => ({ people: response}));
else { // your error handling goes here
}
});
}
If you want to return a promise from your api, you should not resolve the promise returned by your axios call in the api. Instead you can do the following:
export function getAllPeople() {
return axios.get("/api/getAllPeople");
}
Then you can resolve in componentDidMount.
componentDidMount() {
getAllPeople()
.then(response => {
this.setState(() => ({ people: response.data}));
})
.catch(error => {
// your error handling goes here
}
}
My suggestion is to use a cutting-edge feature of React. Error Boundaries
This is an example of using this feature by Dan Abramov.
In this case, you can wrap your component with this Error Boundary component.
What is special for catching the error in axios is that you can use
interceptors for catching API errors.
Your Error Boundary component might look like
import React, { Component } from 'react';
const errorHandler = (WrappedComponent, axios) => {
return class EH extends Component {
state = {
error: null
};
componentDidMount() {
// Set axios interceptors
this.requestInterceptor = axios.interceptors.request.use(req => {
this.setState({ error: null });
return req;
});
this.responseInterceptor = axios.interceptors.response.use(
res => res,
error => {
alert('Error happened');
this.setState({ error });
}
);
}
componentWillUnmount() {
// Remove handlers, so Garbage Collector will get rid of if WrappedComponent will be removed
axios.interceptors.request.eject(this.requestInterceptor);
axios.interceptors.response.eject(this.responseInterceptor);
}
render() {
let renderSection = this.state.error ? <div>Error</div> : <WrappedComponent {...this.props} />
return renderSection;
}
};
};
export default errorHandler;
Then, you can wrap your root component passing axios instance with it
errorHandler(Checkout, api)
As a result, you don't need to think about error inside your component at all.
You could check the response before setting it to state. Something like
componentDidMount() {
getAllPeople().then(response => {
// check if its actual response or error
if(error) this.setState(() => ({ error: response }));
else this.setState(() => ({ people: response}));
});
}
Its relying on the fact that axios will return different objects for success and failures.
The solution from Yevhenii Herasymchuk was very close to what I needed however, I aimed for an implementation with functional components so that I could use Hooks and Redux.
First I created a wrapper:
export const http = Axios.create({
baseURL: "/api",
timeout: 30000,
});
function ErrorHandler(props) {
useEffect(() => {
//Request interceptor
http.interceptors.request.use(function (request) {
// Do something here with Hooks or something else
return request;
});
//Response interceptor
http.interceptors.response.use(function (response) {
if (response.status === 400) {
// Do something here with Hooks or something else
return response;
}
return response;
});
}, []);
return props.children;
}
export default ErrorHandler;
Then I wrapped the part of the project that I needed to check how axios behaved.
<ErrorHandler>
<MainPage/>
</ErrorHandler>
Lastly, I import the axios instance(http) wherever I need it in the project.
Hope it helps anyone that wishes for a different approach.
I just combined both answers by Yevhenii Herasymchuk and GeorgeCodeHub, fixed some mistakes and ported it into React hooks. So here is my final working version:
// [project-directory]/src/lib/axios.js
import Axios from 'axios';
const axios = Axios.create({
baseURL: process.env.NEXT_PUBLIC_BACKEND_URL,
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
withCredentials: true,
});
export default axios;
// [project-directory]/src/components/AxiosErrorHandler.js
import {useEffect} from 'react';
import axios from '#/lib/axios';
const AxiosErrorHandler = ({children}) => {
useEffect(() => {
// Request interceptor
const requestInterceptor = axios.interceptors.request.use((request) => {
// Do something here with request if you need to
return request;
});
// Response interceptor
const responseInterceptor = axios.interceptors.response.use((response) => {
// Handle response here
return response;
}, (error) => {
// Handle errors here
if (error.response?.status) {
switch (error.response.status) {
case 401:
// Handle Unauthenticated here
break;
case 403:
// Handle Unauthorized here
break;
// ... And so on
}
}
return error;
});
return () => {
// Remove handlers here
axios.interceptors.request.eject(requestInterceptor);
axios.interceptors.response.eject(responseInterceptor);
};
}, []);
return children;
};
export default AxiosErrorHandler;
Usage:
// Wrap it around your Layout or any component that you want
return (
<AxiosErrorHandler>
<div>Hello from my layout</div>
</AxiosErrorHandler>
);

Resources