I'm getting data from an API and initially when console it in fetchData function it works but when console it in fetchDailyData function and call this function in another component it didn't work.
How can I solve this issue?
import axios from 'axios';
const url = `https://covid19.mathdro.id/api`;
export const fetchData = async () => {
try {
const { data: { confirmed, recovered, deaths, lastUpdate }} = await axios.get(url);
return { confirmed, recovered, deaths, lastUpdate };
} catch (error) {
}
}
export const fetchDailyData = async () => {
try {
const { data } = await axios.get(`${url}/daily`);
console.log(data); // <<==>> chrome browser is not showing this console log
// fetchDailyData function called in another component
} catch (error) {
}
}
Calling fetchDailyData function in another component
when I call console.log, I can't see the data in console of my browser
const Chart = () => {
const [dailyData, setDailyData] = useState({});
useEffect(() => {
const fetchApi = async () => {
setDailyData(await fetchDailyData());
}
console.log(dailyData);
fetchApi();
});
};
https://covid19.mathdro.id/api/daily which is your url in fetchDailyData doesn't return any data currently at all.
I suppose you have to check if this backend still available. And it is a good practice to check the response status (normally it should return statusCode 200) in response callback.
Related
I'm new to react-query and I'm trying to move all of my API calls into a new file, out of the useQuery calls.
Unfortunately when I do this all of my data is undefined.
I do see the network calls in the network tab, it just isn't being set properly in useQuery.
Thanks in advance for any help on how to change my code to fix this!
// this works
const { loading, data, error } = useQuery([conf_id], async () => {
const { data } = await axios.get(API_URL + '/event/' + conf_id)
return data
});
// this doesn't work - data is undefined
const axios = require('axios');
const getEventById = async () => {
const { data } = await axios.get(API_URL + '/event/2541' + '?noyear=true');
return data.data;
};
const { loading, data, error } = useQuery('conf_id', getEventById});
// the below variants don't work either
// const { loading, data, error } = useQuery('conf_id', getEventById()});
// const { loading, data, error } = useQuery('conf_id', async () => await getEventById()});
// const { loading, data, error } = useQuery('conf_id', async () => await
// const { data } = getEventById(); return data
// });
An AxiosResponse has a data attribute from which you can access the actual API JSON response.
Like you pointed out, this:
async () => {
const { data } = await axios.get(API_URL + '/event/' + conf_id)
return data
}
Should suffice for the fetching function.
So the final implementation should look like
const axios = require('axios');
const getEventById = async () => {
const { data } = await axios.get(API_URL + '/event/2541' + '?noyear=true');
return data;
};
const { loading, data, error } = useQuery('conf_id', getEventById);
The data you get from the useQuery should be undefined on the first render and once the server responds it will change to whatever the response is.
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { accounts } = await res.json();
setAccounts(accounts);
console.log(accounts);
}
fetchAccounts();
}, []);
}
I'm trying to understand why console.log shows nothing in this example and what is the correct way to console.log the data that is being fetched from the api.
Well, you need to get the structure of the returned payload from the API correct. It does not have an accounts property.
The payload looks like this:
{
"success":true,
"data":[{"account":"joejerde","assets":"11933"},{"account":"protonpunks","assets":"9072"}],
"queryTime": 1646267075822
}
So you can rename the data property while destructuring. const { data: accountList } = await res.json();
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { data: accountList } = await res.json();
setAccounts(accountList);
// logging both the state and the fetched value
console.log(accounts, accountList);
// accounts (state) will be undefined
// if the fetch was successful, accountList will be an array of accounts (as per the API payload)
}
fetchAccounts()
}, [])
return <div>
{JSON.stringify(accounts)}
</div>
}
Edit: using some other variable name while destructuring, confusing to use the same variable name as the state (accounts).
Working codesandbox
One thing I would change is working with try/catch surrounding async/await statements.
If your await statement fails it will never reach the console.log statement.
Unless you have another component handling those errors, I would use it in that way.
That is my suggestion:
function UserAccounts() {
const [accounts, setAccounts] = useState();
useEffect(() => {
try {
async function fetchAccounts() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicassets/v1/accounts'
);
const { accounts } = await res.json();
setAccounts(accounts);
console.log(accounts);
}
} catch (err) {
console.log(err)
// do something like throw your error
}
fetchAccounts();
}, []);
}
since state function runs asyncronousely . therefore when you use setAccounts it sets accounts variable in async way , so there is a preferred way of doing this thing is as below
problems i seen
1.fetch result should destructured with data instead of accounts variable
2.setAccounts function is running async way so it will not print result immedietly in next line
import { useEffect, useState } from "react";
export default function App() {
const [accounts, setAccounts] = useState();
async function fetchAccounts() {
const res = await fetch(
"https://proton.api.atomicassets.io/atomicassets/v1/accounts"
);
const { data } = await res.json();
setAccounts(data);
}
// on component mount / onload
useState(() => {
fetchAccounts();
}, []);
// on accounts state change
useEffect(() => {
console.log(accounts);
}, [accounts]);
return <div className="blankElement">hello world</div>;
}
check here sample
I have a custom hook like so for getting data using useQuery. The hook works fine, no problem there.
const getData = async (url) => {
try{
return await axios(url)
} catch(error){
console.log(error.message)
}
}
export const useGetData = (url, onSuccess) => {
return useQuery('getData', () => getData(url), {onSuccess})
}
However, if I call this hook twice in my component it will only fetch data from the first call even with a different URL. (Ignore the comments typo, that's intentional)
The call in my component:
const { data: commentss, isLoading: commentsIsLoading } = useGetData(`/comments/${params.id}`)
const { data: forumPost, isLoading: forumPostIsLoading } = useGetData(`/forum_posts/${params.id}`)
When I console.log forumPost in this case, it is the array of comments and not the forum post even though I am passing in a different endpoint.
How can I use this hook twice to get different data? Is it possible? I know I can just call parallel queries but I would like to use my hook if possible.
Since useQuery caches based on the queryKey, use the URL in that name
const getData = async(url) => {
try {
return await axios(url)
} catch (error) {
console.log(error.message)
}
}
export const useGetData = (url, onSuccess) => {
return useQuery('getData' + url, () => getData(url), {
onSuccess
})
}
//........
const {
data: commentss,
isLoading: commentsIsLoading
} = useGetData(`/comments/${params.id}`)
const {
data: forumPost,
isLoading: forumPostIsLoading
} = useGetData(`/forum_posts/${params.id}`)
I have a problem where useQuery is always running in my application and I don't why
In my component
import { GET_DATA } from 'apiCalls';
const { loading, error, data } = useQuery('getData', GET_DATA(token));
In my api call
export const GET_DATA = async (token) => {
try {
const res = await axios.get(`${process.env.REACT_APP_SERVER}/api/...`, {
headers: {'auth-token': token},
});
console.log(res);
return res.data;
} catch (err) {
console.log('Error getting data');
return err;
}
}
when I debug my app. The function GET_DATA is always running ALL the time. what is the issue here ?
You must provide the useQuery only the function it wants to run, you must not call it inside useQuery. Provide the token to GET_DATA this way:
EDIT
As #tkdodo said we don't need to use the async function.
const { loading, error, data } = useQuery('getData', ()=>{
return GET_DATA(token);
});
The first solution I provided was this:
const { loading, error, data } = useQuery('getData', async()=>{
const data = await GET_DATA(token);
return data;
});
The root cause is the same as in React-Query, useQuery returns undefined only after loading is complete
The queryFn needs to be a function that returns a promise. GET_DATA does that. But by doing
GET_DATA(token) you directly invoke the function. So you’ll likely want:
() => GET_DATA(token) instead.
Try the following:
// apiCalls.js
export const getData = async (token) => {
try {
const res = await axios.get(`${process.env.REACT_APP_SERVER}/api/...`, {
headers: {'auth-token': token},
});
return res.data;
} catch (err) {
console.log('Error getting data');
return err;
}
// Component.js
import { getData } from 'apiCalls';
function Component(){
const { loading, error, data } = useQuery(
'getData',
()=>GET_DATA(token)
);
return (
<div>...</div>
)
}
useQuery should run in the component and the second parameter should not be a promise, but a function that returns a promise.
Trying to mock GET request to API but always get
Timeout - Async callback was not invoked within the 10000ms timeout specified by jest.setTimeout.
even though I increased the timeout it still throws error.
Hook
export default function apiCaller() {
const [rawApiData, setRawApiData] = useState({});
const [errorMsg, setErrorMsg] = useState('');
const callApi = async (inputValue) => {
try {
const apiData= await axios.get(
`https://cloud.iexapis.com/stable/stock/market/batch?types=chart&symbols=${inputValue}&range=3m&token=lalaccf0`
);
setRawApiData(apiData);
} catch (err) {
setErrorMsg(
'Error occured!! ' +
(Boolean(err.response) ? err.response.data : err.message)
);
}
};
return { rawApiData, callApi, errorMsg };
}
Axios mock
export default {
get: jest.fn().mockResolvedValue({ data: {} }),
};
Test
import { renderHook, act } from 'react-hooks-testing-library';
import apiCaller from '../components/stock-chart/stockApiCaller';
import axios from 'axios';
jest.mock('axios');
it('should set error properly when api call is unsuccessfull because of bad data', async () => {
axios.get.mockResolvedValueOnce({ data: { test: '123' } });
const { result, waitForNextUpdate } = renderHook(() => apiCaller());
act(() => result.current.callApi('fb/tsla'));
await waitForNextUpdate();
expect(result.current.rawApiData.data.test)
.toBe(123)
}, 10000);
I finally got the issue resolved. There is new way to write act() i.e. async act(). Please find below the updated version of test which works fine.
it('should set rawData properly when api call is successfull because of', async () => {
axios.get.mockResolvedValueOnce({ data: { test: '123' } });
const { result, waitForNextUpdate } = renderHook(() => apiCaller());
await act(async () => {
result.current.callApi('fb/tsla');
await waitForNextUpdate();
});
expect(result.current.rawApiData.data.test).toBe('123');
});
Update react to 16.9.0-alpha.0
https://github.com/facebook/react/releases/tag/v16.9.0-alpha.0