I'm running into errors when I try to call an api endpoint such as the one below in a utility file. What I mean is I have a file which is not a page but only stores API calls.
export const getProducts = async () => {
const resp = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/products`);
const products = await resp.json();
return products;
}
Is this not allowed? Whenever I try it in production it's giving me a 504 time out, whereas if I put this method directly inside the page file and call it, it's fine. On localhost either way works.. Can someone clear this up for me?
Example:
---- Option 1 ----
Everything in React component (works)
export const getProducts = async () => {
const resp = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/products`);
const products = await resp.json();
setProducts(products);
}
// Works
useEffect(() => {
getProducts();
}, []);
----- Option 2 (does not work in production(!)) -----
Utility.js
export const getProducts = async (setProducts) => {
const resp = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/products`);
const products = await resp.json();
setProducts(products);
}
React component
// Does not work
useEffect(() => {
getProducts(setProducts);
}, []);
Related
I am facing issues mocking a method which internally makes call to database to fetch data. Please find the code below:
const getData = async (...args) => {
// first call
await db.getData();
// second call
await db.queryData();
return data;
}
const generateResult = async (...args) => {
const data = await getData(...args);
// rest of the code
return result;
}
export ClassTest;
In the test file:
describe(('Verify generateResult') => {
jest.spyOn(generateResult, 'getData').mockImplementation((...args) => {
return data;
});
it('should return result', async () => {
const result = await generateResult(..args);
expect(result).toBeTruthy();
});
});
The getData method makes database calls to retrieve data. Rest of the code just massages the data and returns result. Even though its been mocked the code fails when the database calls are made. I assume that it should get data from mocked implementation and rest of the code should execute. Not sure what is wrong here. Could anyone pls let me know. Do not have a lot of experience writing jest test cases.
thanks
Any chance that you could move the getData method to another module? Then you can mock the getData and the imported version would be the mocked one everywhere. Just an idea.
getData.js
export const getData = async (...args) => {
const data = await Promise.resolve(false);
console.log('original called')
return data;
}
dbUtils.js
import { getData } from './getData'
export const generateResult = async (...args) => {
const data = await getData(...args);
return data;
}
and the test:
import * as getDataUtils from '../getData';
import { generateResult } from '../dbUtils';
it('should return result', async () => {
jest.spyOn(getDataUtils, 'getData').mockResolvedValue(true);
const result = await generateResult();
expect(result).toBeTruthy();
});
I have an async function that calls an api getting me the current role of a user. Later on I want to attach that role to a variable. Here's my code
const getRole = async () => {
const response = await roleService.getRole();
const roles = await response.role
return roles
}
...........
const currentRole = getRole() //I want this to be the value from return roles
I'm new to react and having trouble with this. How can I set currentRole to the value in return roles?
I would opt to save the information that you got from the API on a state
const [roles, setRoles] = useState();
const getRole = async () => {
const response = await roleService.getRole();
const roles = await response.role
setRoles(roles);
}
you can call the gerRole function on a useEffect like this
useEffect(() => {
getRole();
}, []);
or you can call the getRole function on a button click
<button onClick={getRole}>Click me to get roles</button>
I want to structure my application with some sort of service class, but whenever I extract my axios calls from the page, then the axios function seems to return "undefined".
My page looks like this. The signin function is called when the user hits the button. When I put the axios call in the page like this, everything works fine. The usestate is updated and displays.
export default function AccountPage() {
const [signinResponse, setSigninResponse] = useState();
async function signin() {
await axios
.get(
`...url...`
)
.then((res) => {
setSigninResponse(res)
});
}
...
However, when I take the axios function and move it to a service class like this
import axios from "axios";
export async function tableauSignin() {
await axios
.get(
`...url...`
)
.then((res) => {
console.log(res);
return res;
});
}
and then import and make the call like this
import { tableauSignin } from "../services/tableau-online.service";
...
export default function AccountPage() {
const [signinResponse, setSigninResponse] = useState();
async function signin() {
const r = await tableauSignin();
setSigninResponse(r);
console.log(r);
}
...
the log from the service class is good but the log on the account page is undefined.
As #RobinZigmond mentioned in comment. The following solution will work but it's a needless.
it's a needless verbose way of just doing export function
tableauSignin() { return axios.get(url).then(response =>
response.data) }.
export async function tableauSignin() {
return await axios.get(url).then(response => response.data)
}
This Solution may be more useful
const getData = async () => {
let res = await axios.get("url");
let { data } = res.data; //or res
return data;
};
You can also import this way
var response = await getData();
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
In my react app, I am currently passing a list of stores by calling the API directly from the URL.
const getStore = async () => {
try {
const response = axios.get(
'http://localhost:3001/appointment-setup/storeList'
);
return response;
} catch (err) {
console.error(err);
return false;
}
};
I pass this function into my useEffect hook where I would set my get a list of stores using resp.data.stores:
const [storeLocations, setStoreLocations] = useState([]);
useEffect(() => {
async function getData(data) {
await service.stepLocation.init();
const resp = await getStore();
setStoreLocations(resp.data.stores);
}
setFlagRender(true);
return getData();
}, []);
This works, however, I noted in useEffect there is a call await service.stepLocation.init(). There is a file that already takes care of all the backend/data for the component.
const stepLocation = {
// removed code
// method to retrieve store list
retrieveStoreList: async function ()
let response = await axios.get(
constants.baseUrl + '/appointment-setup/storeList'
);
return response.data.stores;
,
// removed code
Since this data is available, I don't need the getStore function. However when I try to replace response.data.stores in useEffect with service.stepLocation.retrieveStoreList no data is returned. How do I correctly pass the data from this file in my useEffect hook?
I think your useEffect should be like follows as you want to save the stores in your state.
useEffect(() => {
const updateStoreLocations = async () => {
const storeLocations = await service.stepLocation.retrieveStoreList();
setStoreLocations(storeLocations);
}
updateStoreLocations();
}, [])