Not able to convert response data into JSON - reactjs

I'm able to get response from API, but not able to convert response into Json and not able to return the data. It simply return null.
const responseData = async () => {
try{
const response = await axios.get('https://randomuser.me/api')
console.log(response) // console object
const jsonData = await response.json()
return jsonData;
}catch(err){
console.error(err)
}
}
export default function App() {
const [randomUserDataJson,setRandomUserDataJson] = useState('')
useEffect( () => {
responseData().then(randomdata => {
setRandomUserDataJson(randomdata || 'not found')
})
}, []);
return (
<div >
<pre>
<p>{randomUserDataJson}</p>
</pre>
</div>
);
}
Output
not found

You can directly return the axios response nothing but the promise and access the result using then method.
import axios from "axios";
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [randomUserDataJson, setRandomUserDataJson] = useState("");
useEffect(() => {
responseData().then((randomdata) => {
const data = JSON.stringify(randomdata.data);
setRandomUserDataJson(data || "not found");
});
}, []);
return (
<div>
<pre>
<p>{randomUserDataJson}</p>
</pre>
</div>
);
}
const responseData = async () => {
try {
const response = await axios.get("https://randomuser.me/api");
return response;
} catch (err) {
console.error(err);
}
};
codesandbox - https://codesandbox.io/s/withered-bush-iicqm?file=/src/App.js

You don't have to do const jsonData = await response.json(), axios will deserialize the response to JS Object for you. Just remove that line and it would work. Also, you can't render JS object as a child of a React Component, so it has to be stringified.
import axios from 'axios';
import { useState, useEffect } from 'react';
const responseData = async () => {
try{
const response = await axios.get('https://randomuser.me/api')
console.log(response) // console object
return response;
}catch(err){
console.error(err)
}
}
export default function App() {
const [randomUserDataJson,setRandomUserDataJson] = useState('')
useEffect( () => {
responseData().then(randomdata => {
setRandomUserDataJson(randomdata || 'not found')
})
}, []);
return (
<div >
<pre>
<p>{JSON.stringify(randomUserDataJson, null, 2)}</p>
</pre>
</div>
);
}

Related

React custom http-hook abortController() bug

I have this custom http hook with abort when you try to go to a different page (I saw it in a tutorial but I am not truly sure I need it). When I fetch data with it and useEffect(), I have this error on the backend but the request is executed and everything is as planned. My question is, how to improve my code so it does not throw this error and do I need this functionality with abortController() ?
http-hook.ts
import { useCallback, useRef, useEffect } from "react";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { selectError, showError } from "src/redux/error";
import { selectLoading, startLoading, stopLoading } from "src/redux/loading";
export const useHttpClient = () => {
const dispatch = useDispatch();
const error = useSelector(selectError);
const loading = useSelector(selectLoading);
const activeHttpRequests: any = useRef([]);
const sendRequest = useCallback(
async (url, method = "GET", body = null, headers = {}) => {
dispatch(startLoading());
const httpAbortCtrl = new AbortController();
activeHttpRequests.current.push(httpAbortCtrl);
try {
const response = await fetch(url, {
method,
body,
headers,
signal: httpAbortCtrl.signal,
});
const responseData = await response.json();
activeHttpRequests.current = activeHttpRequests.current.filter(
(reqCtrl) => reqCtrl !== httpAbortCtrl
);
if (!response.ok) {
throw new Error(responseData.message);
}
dispatch(stopLoading());
return responseData;
} catch (err) {
dispatch(showError(err.message));
dispatch(stopLoading());
throw err;
}
},
[]
);
useEffect(() => {
return () => {
activeHttpRequests.current.forEach((abortCtrl: any) => abortCtrl.abort());
};
}, []);
return { loading, error, sendRequest };
};
UserInfo.tsx
import React, { Fragment, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { useHttpClient } from "src/hooks/http-hook";
import classes from "./UserInfo.module.css";
const UserInfo = () => {
const { sendRequest } = useHttpClient();
const [currentUser, setCurrentUser] = useState<any>();
const userId = useParams<any>().userId;
useEffect(() => {
const fetchCurrentUser = async () => {
try {
const responseData = await sendRequest(
`http://localhost:5000/api/user/${userId}`
);
setCurrentUser(responseData.user);
console.log("currentUser ", currentUser);
} catch (err) {
console.log(err);
}
};
fetchCurrentUser();
}, [sendRequest ,userId]);
return currentUser ? (
<Fragment>
<div className={classes.cover} />
<div className={classes.user_info}>
<img
alt="user_img"
src={`http://localhost:5000/${currentUser.image}`}
className={classes.user_img}
/>
<div className={classes.text}>
<p>
Name: {currentUser.name} {currentUser.surname}
</p>
<p>Email: {currentUser.email}</p>
<p>Age: {currentUser.age}</p>
</div>
</div>{" "}
</Fragment>
) : (
<p>No current user</p>
);
};
export default UserInfo;
Backend
getCurrentUser.ts controller
const getCurrentUser = async (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
const userId = req.params.userId;
let user;
try {
user = await User.findById(userId);
} catch (err) {
const error = new HttpError("Could not fetch user", 500);
return next(error);
}
res.json({ user: user.toObject({ getters: true }) });
};

map array of objects from an api react

I have been trying to get the images from this API to append to the page by mapping through them, but I keep getting one of two error messages saying 'undefined.map is not a function' or 'getBirds.map is not a function.'I've tried leaving the array of objects as it is and setting state to an object and an array(at separate times) but that didn't work. I've also tried using Object.key, Object.values, and Object.entries(each at separate times) to turn the array of objects into an array and then map through my variable and through getBirds(again separately) but those attempts also failed. I have attached three of my attempts below. Can someone help me understand where I've gone wrong?
// Attempt 1
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 2
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState([])
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={getBirds.map(image)} alt={getBirds.map(image)}></img>
</div>
)
}
export default Birds
// Attempt 3
import {useState, useEffect} from 'react'
import axios from 'axios'
function Birds(props) {
const [getBirds, setGetBirds] = useState({})
const {image} = props
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
const birds = Object.entries(getBirds)
birds.forEach(([key, value]) => {
console.log(key, value)
})
if (!getBirds) return <h3>Loading...</h3>
return (
<div>
<img src={birds.map(image)} alt={birds.map(image)}></img>
</div>
)
}
export default Birds
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You would need to initialize your state with an array, so the map function won't get errors, and correct the way you map it:
Initialize state with an array:
const [getBirds, setGetBirds] = useState([]);
Map it:
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
Also, check your array with length, because [] or {} both equal to true.
if (!getBirds.length) return <h3>Loading...</h3>;
console.log(!![]);
console.log(!!{});
console.log(!![].length)
The completed solution:
import { useState, useEffect } from "react";
import axios from "axios";
function Birds(props) {
const [getBirds, setGetBirds] = useState([]);
useEffect(() => {
async function fetchBirds() {
const URL = 'https://audubon-society-api.herokuapp.com/birds';
try {
const res = await axios.get(URL);
console.log(res.data);
setGetBirds(res.data);
} catch (error) {
console.log(error);
}
}
fetchBirds();
}, []);
if (!getBirds.length) return <h3>Loading...</h3>;
return (
<div>
{getBirds.map((bird) => (
<img src={bird.image} alt={bird.image}></img>
))}
</div>
);
}
export default Birds;
Working Example:
Your init state of birds and setBirds should be an array [] not an object {}, also you don't need:
const birds = Object.entries(getBirds). fetch return array of birds already.
<img src={birds.map(image)} alt={birds.map(image)}></img> is wrong, the array loop map should render an image for each bird.
Below code will run for your need:
import React, {useState, useEffect} from "react";
import axios from 'axios';
function Birds(props) {
//- const [getBirds, setGetBirds] = useState([])
//- const {image} = props
// +
const [birds, setGetBirds] = useState([])
useEffect(() => {
async function fetchBirds() {
const URL = `https://audubon-society-api.herokuapp.com/birds`
try {
const res = await axios.get(URL)
console.log(res.data)
setGetBirds(res.data)
} catch (error) {
console.log(error)
}
}
fetchBirds()
}, [])
// - const birds = Object.entries(getBirds)
// - birds.forEach(([key, value]) => {
// - console.log(key, value)
// - })
// - if (!getBirds) return <h3>Loading...</h3>
if (!birds) return <h3>Loading...</h3>
return (
<div>
{/* <img src={birds.map(image)} alt={birds.map(image)}></img> */}
{birds.map((item, index) =>
<img src={item.image} alt={index}></img>
)}
</div>
)
}
export default Birds

Setting the type to the incoming apiye state

I want to put an incoming API to the status but it says undefined.
I want to discard my data coming to randomcocktail and then use it.
Random.tsx
import { useState, useEffect } from "react";
import { CocktailType } from "../Utils/data";
import "../Style/index.scss";
const Random = () => {
const [randomCocktail, setRandomCocktail] = useState<CocktailType[]>();
useEffect(() => {
const getRand = async () => {
const response = await fetch(
"https://www.thecocktaildb.com/api/json/v1/1/random.php"
);
const data = await response.json();
const { cocktail = [] } = data;
setRandomCocktail(cocktail[0]);
console.log(data);
console.log(randomCocktail);
};
getRand();
}, []);
return (
<div className="randomPage">
<div className="leftRand"></div>
<div className="rightRand"></div>
</div>
);
};
export default Random;
data.ts
export type CocktailType={
strDrink :string;
strTag:string|null;
strVideo:string|null;
strCategory:string;
strIBA:string|null;
strAlcoholic:string;
strGlass:string;
strInstructions:string;
strDrinkThumb:string;
}
api : https://www.thecocktaildb.com/api/json/v1/1/random.php
Try this
import { useState, useEffect } from "react";
import { CocktailType } from "../Utils/data";
import "../Style/index.scss";
const Random = () => {
const [randomCocktail, setRandomCocktail] = useState<CocktailType[]>();
useEffect(() => {
const getRand = async () => {
const response = await fetch(
"https://www.thecocktaildb.com/api/json/v1/1/random.php"
);
const data = await response.json();
const cocktail = data.drinks;
setRandomCocktail(cocktail[0]);
console.log(data);
console.log(randomCocktail);
};
getRand();
}, []);
return (
<div className="randomPage">
<div className="leftRand"></div>
<div className="rightRand"></div>
</div>
);
};
export default Random;

Using Axios in a React Function

I am trying to pull data from an Axios Get. The backend is working with another page which is a React component.
In a function however, it doesn't work. The length of the array is not three as it is supposed to be and the contents are empty.
I made sure to await for the axios call to finish but I am not sure what is happening.
import React, { useState, useEffect } from "react";
import { Container } from "#material-ui/core";
import ParticlesBg from "particles-bg";
import "../utils/collagestyles.css";
import { ReactPhotoCollage } from "react-photo-collage";
import NavMenu from "./Menu";
import { useRecoilValue } from "recoil";
import { activeDogAtom } from "./atoms";
import axios from "axios";
var setting = {
width: "300px",
height: ["250px", "170px"],
layout: [1, 3],
photos: [],
showNumOfRemainingPhotos: true,
};
const Collages = () => {
var doggies = [];
//const [dogs, setData] = useState({ dogs: [] });
const dog = useRecoilValue(activeDogAtom);
const getPets = async () => {
try {
const response = await axios.get("/getpets");
doggies = response.data;
//setData(response.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
useEffect(() => {
const fetchData = async () => {
getPets();
};
fetchData();
}, []);
return (
<>
<NavMenu />
<ParticlesBg type="circle" margin="20px" bg={true} />
<br></br>
<div>
{doggies.length === 0 ? (
<div>Loading...</div>
) : (
doggies.map((e, i) => {
return <div key={i}>{e.name}</div>;
})
)}
</div>
<Container align="center">
<p> The length of dogs is {doggies.length} </p>
<h1>Knight's Kennel</h1>
<h2> The value of dog is {dog}</h2>
<h2>
Breeders of high quality AKC Miniature Schnauzers in Rhode Island
</h2>
<section>
<ReactPhotoCollage {...setting} />
</section>
</Container>
</>
);
};
export default Collages;
Try doing the following:
const [dogs, setData] = useState([]);
[...]
const getPets = async () => {
try {
const response = await axios.get("/getpets");
doggies = response.data;
setData(response.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
const fetchData = async () => {
getPets();
};
useEffect(() => {
fetchData();
}, []);
No idea if it will actually work, but give it a try if you haven't.
If you don't use useState hook to change the array, it won't update on render, so you will only see an empty array on debug.
As far as I can tell you do not return anything from the getPets() function.
Make use of the useState Function to save your doggies entries:
let [doggies, setDoggies ] = useState([]);
const getPets = async () => {
try {
const response = await axios.get("/getpets");
return response.data;
} catch (err) {
// Handle Error Here
console.error(err);
}
return []
};
useEffect(() => {
setDoggies(await getPets());
});
I used setState inside the getPets function. Now it works.
const Collages = () => {
const [dogs, setData] = useState([]);
const dog = useRecoilValue(activeDogAtom);
const getPets = async () => {
try {
const response = await axios.get("/getpets");
setData(response.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
useEffect(() => {
const fetchData = async () => {
getPets();
};
fetchData();
}, []);

How to use Axios cancelToken in interceptors?

In ReactJs I am using Axios to getting data from API. I need to use cancelToken when I try to make the duplicate requests. E.g: suppose I am on the homepage before complete Axios request, I am requested for About page. As a result, the React app showing memory leaking error. So, my plan is to set Axios cancelToken in Axios interceptors. I have tried but, it is not working for me.
requestApi.js
import axios from 'axios';
const requestApi = axios.create({
baseURL: process.env.REACT_APP_API_URL
});
const source = axios.CancelToken.source();
requestApi.interceptors.request.use(async config => {
const existUser = JSON.parse(localStorage.getItem('user'));
const token = existUser && existUser.token ? existUser.token : null;
if (token) {
config.headers['Authorization'] = token;
config.headers['cache-control'] = 'no-cache';
}
config.cancelToken = source.token;
return config;
}, error => {
return Promise.reject(error);
});
requestApi.interceptors.request.use(async response => {
throw new axios.Cancel('Operation canceled by the user.');
return response;
}, error => {
return Promise.reject(error);
});
export default requestApi;
Dashboard.js
import requestApi from './requestApi';
useEffect(() => {
const fetchData = async () => {
try {
const res = await requestApi.get('/dashboard');
console.log(res.data);
} catch (error) {
console.log(error);
}
}
fetchData();
}, []);
in case you still need it or if someone else comes looking for this. This is how it has worked for me.
import axios from "axios";
// Store requests
let sourceRequest = {};
const requestApi = axios.create({
baseURL: process.env.REACT_APP_API_URL
});
requestApi.interceptors.request.use(
async config => {
const existUser = JSON.parse(localStorage.getItem("user"));
const token = existUser && existUser.token ? existUser.token : null;
if (token) {
config.headers["Authorization"] = token;
config.headers["cache-control"] = "no-cache";
}
return config;
},
error => {
return Promise.reject(error);
}
);
requestApi.interceptors.request.use(
request => {
// If the application exists cancel
if (sourceRequest[request.url]) {
sourceRequest[request.url].cancel("Automatic cancellation");
}
// Store or update application token
const axiosSource = axios.CancelToken.source();
sourceRequest[request.url] = { cancel: axiosSource.cancel };
request.cancelToken = axiosSource.token;
return request;
},
error => {
return Promise.reject(error);
}
);
export default requestApi;
This may not be a stable solution, but we can use some magic to make a component that terminates async code (including requests) running inside when the component unmounts. No tokens required to make it work. See a Live Demo
import React, { useState } from "react";
import { useAsyncEffect, E_REASON_UNMOUNTED } from "use-async-effect2";
import { CanceledError } from "c-promise2";
import cpAxios from "cp-axios";
function* makeAPICall(url) {
const existUser = JSON.parse(localStorage.getItem("user"));
const token = existUser && existUser.token ? existUser.token : null;
return yield cpAxios(url, {
headers: {
Authorization: token,
"cache-control": "no-cache"
}
});
}
export default function TestComponent(props) {
const [text, setText] = useState("");
const cancel = useAsyncEffect(
function* () {
console.log("mount");
this.timeout(props.timeout);
try {
setText("fetching...");
const response = yield* makeAPICall(props.url);
setText(`Success: ${JSON.stringify(response.data)}`);
} catch (err) {
CanceledError.rethrow(err, E_REASON_UNMOUNTED); //passthrough
setText(`Failed: ${err}`);
}
return () => {
console.log("unmount");
};
},
[props.url]
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{text}</div>
<button onClick={cancel}>Abort</button>
</div>
);
}

Resources