Axios API call returns a 404 on page render, but still returns api objects in terminal - reactjs

I am using axios to make an api call to an api found on Apihub for a next JS app.
here is the code for the function to make the call to provide a list of property JSON objects.
export const baseUrl = "https://zillow56.p.rapidapi.com"
export const fetchApiListsingsCustom = async (url) => {
const { data } = await axios.get((url), {
method: 'GET',
headers: {
'X-RapidAPI-Key': '328713ab01msh862a3ad609011efp17e6b4jsn0e7112d5ee9a',
'X-RapidAPI-Host': 'zillow56.p.rapidapi.com'
}
});
data.then((res) => {
console.log(res);
})
.catch((error) => {
console.error(error);
});
return data.json();
}
When rendering the page I'm attempting to inject the response's data to dynamically create a list of apartment listings.
I'm trying to use getServerSideProps so that the data is already available by the time a user requests the page. After fetching the data, I want to also print them in the terminal to validate it's success.
export default function Home({ propertiesCustomdata })
export async function getServerSideProps() {
const propertiesCustom = await fetchApiListsingsCustom(`${baseUrl}`)
const propertiesCustomdata = propertiesCustom.json()
return {
props: {
propertiesCustomdata
}
}
}
The problem is, I seem to be getting a 404 error from the axios call, before the page gets a chance to load. When I access this I get a 404 error but I also manage to receive some contents of the call the API was to make.
My apologies if this is unclear, but this is all I know to report on this so far.
Studying async and await, fetch, and axios. Very confusing.

Related

react-query: useQuery returns undefined and component does not rerender

I'm playing around with reactQuery in a little demo app you can see in this repo. The app calls this mock API.
I'm stuck on a an issue where I'm using the useQuery hook to call this function in a product API file:
export const getAllProducts = async (): Promise<Product[]> => {
const productEndPoint = 'http://localhost:5000/api/product';
const { data } = await axios.get(productEndPoint);
return data as Array<Product>;
};
In my ProductTable component I then call this function using:
const { data } = useQuery('products', getAllProducts);
I'm finding the call to the API does get made, and the data is returned. but the table in the grid is always empty.
If I debug I'm seeing the data object returned by useQuery is undefined.
The web request does successfully complete and I can see the data being returned in the network tab under requests in the browser.
I'm suspecting its the way the getAllProducts is structured perhaps or an async await issue but can't quite figure it out.
Can anyone suggest where IO may be going wrong please?
Simply use like this
At first data is undefined so mapping undefined data gives you a error so we have to use isLoading and if isLoading is true we wont render or map data till then and after isLoading becomes false then we can render or return data.
export const getAllProducts = async (): Promise<Product[]> => {
const productEndPoint = 'http://localhost:5000/api/product';
const res= await axios.get(productEndPoint);
return res.data as Array<Product>;
};
const { data:products , isLoading } = useQuery('products', getAllProducts);
if(isLoading){
return <FallBackView />
}
return (){
products.map(item => item)
}
I have managed to get this working. For the benefits of others ill share my learnings:
I made a few small changes starting with my api function. Changing the function to the following:
export const getAllProducts = async (): Promise<Product[]> => {
const response = await axios.get(`api/product`, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
return response.data as Product[];
};
I do not de-construct the response of the axios call but rather take the data object from it and return is as an Product[]
Then second thing I then changed was in my ProductTable component. Here I told useQuery which type of response to expect by changing the call to :
const { data } = useQuery<Product[], Error>('products', getAllProducts);
Lastly, a rookie mistake on my part: because I was using a mock api in a docker container running on localhost and calling it using http://localhost:5000/api/product I was getting the all to well known network error:
localhost has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present...
So to get around that for the purpose of this exercise I just added a property to the packages.json file: "proxy":"http://localhost:5000",
This has now successfully allowed fetching of the data as I would expect it.

How can I optimize my code to stop sending GET requests constantly?

I am using the Yelp Fusion API to get a list of restaurants from Yelp. However, I am always constantly sending a GET request and I am not sure what is going on or how to fix it. I have tried React.memo and useCallback. I think the problem lies within how I am making the call rather than my component rerendering.
Here is where I send a GET request
// Function for accessing Yelp Fusion API
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
// Saving our results, getting first 5 restaurants,
// and turning off our loading screen
setYelpResults({businesses: response.data.businesses.splice(0, 5)});
setEnableLoading(1);
}
catch (error) {
setEnableLoading(2);
}
};
This is where I use axios.
// Our Yelp Fusion code that sends a GET request
export default axios.create({
baseURL: `${'https://cors-anywhere.herokuapp.com/'}https://api.yelp.com/v3`,
headers: {
Authorization: `Bearer ${KEY}`
},
})
You are probably calling that function within your functional component and that function sets a state of that component, so it re-renders. Then the function is executed again, sets state, re-renders and so on...
What you need to do is to wrap that API call inside a:
useEffect(() => {}, [])
Since you probably want to call it one time. See useEffect doc here
You can do 2 things either use a button to get the list of restaurants because you are firing your function again and again.
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
Use a button instead maybe so once that button is clicked function is fired.
<button onClick={yelpFusionSearch} />Load More Restaurants </button>
Use your fuction inside useEffect method which will load 5 restaurants once the page renders
useEffect(() => {
const yelpFusionSearch = async () => {
try {
const response = await yelp.get('/businesses/search', {
params: {
term: food,
location: location
}
})
}, [])

Cypress stub function gets lost after loading my app

In my main React app's class componentDidMount I call an api method to fetch some data. I'm trying to test that my app does the right thing given the data. Rather than try and mock the server, and deal with Cypress's semi-support for fetch and whatnot, I'm trying to cy.stub the entire API function to just return a block of data.
// api.ts
export const fetchData = async (): Promise<IData> => {
...
}
// app.tsx
import { fetchData } from "../api";
export class App extends React.PureComponent<IProps, IState> {
async componentDidMount() {
const data = await fetchData();
// ...
}
}
// testData.test.ts
import * as Api from "../../src/api";
context("Test the app after loading mock data from the API", () => {
describe("Calling the API",() => {
before(() => {
cy.stub(Api, "fetchData", () => {
return Promise.resolve({
someData: "value"
});
});
cy.visit("/");
});
it("calls 'fetchData'", () => {
expect(Api.fetchData).to.be.called;
});
});
});
However, the app still calls the original version of fetchData instead of the stubbed version.
I tried experimenting by writing a test that simply calls a library method that itself imports fetchData, and that time the mock worked fine. So mocking an ES6 function that way should work. So it's something to do with loading my application that causes it to get lost.
This is not possible with cy.visit. You can use the new plugin #cypress/react to do the trick ;)
That's not really how the stubbing is supposed to work:
https://docs.cypress.io/guides/guides/network-requests.html#Stubbing
To begin stubbing responses you need to do two things.
Start a cy.server()
Provide a cy.route()
cy.server() // enable response stubbing
cy.route({
method: 'GET', // Route all GET requests
url: '/users/*', // that have a URL that matches '/users/*'
response: [] // and force the response to be: []
})
Not sure which API call you're trying to do, but this is a great headstart, and it doesn't require to have cypress any knowledge of your internal api work.
Not really knowing what you are trying to test, but a complete example would be this:
describe("Calling the API",() => {
cy.server() // enable response stubbing
cy.route({
method: 'GET', // Route all GET requests
url: '/users/*', // that have a URL that matches '/users/*'
response: [] // and force the response to be: []
})
.as('get-user')
.visit('/')
.wait('#get-user') // wait for your call to finish and assert it has been called
})

Any way at all to specify a callback to wrap the response with?

So I am trying to develop a search bar component in a React application where you can type in a users last name and that request will go to the Behance API and pull up that users data.
I am stuck on this:
axios
.get(API_URL + 'users?q=' + 'matias' + '&client_id=' + API_KEY + '&callback=')
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
alert(error.message);
});
I have tried wrapping the above in a const userSearch = () => {}, but that takes me a step farther from my goal. With the above I actually do get 200 statuses, but there is the CORS issue. I just can't seem to put together a callback that is not undefined in there, nevermind that this is a search bar implementation so I am going to have to refactor the above. I was just wanting to see some data returned.
One of the nicest things in Axios, is the seperation between request argument.
For example, the url should be only URL: API_URL + '/users'.
The parameters you want to pass, should be sent as an object.
The promise of the axios get, is the callback you are looking for.
Therefore, your request should look like this:
axios.get(API_URL + 'users', {
params: {
q: 'matias',
client_id: API_KEY,
}
})
.then(response => {
- success callback actions -
})
.catch(error => {
- error callback actions -
});
So I had refactored my axios code and I was still getting CORS errors, but after reading a couple of blogs online saying that with fetch() and jQuery you could get around that, in particular this SO article: Loading Data from Behance API in React Component
I actually duplicated Yasir's implementation like so:
import $ from 'jquery';
window.$ = $;
const API_KEY = '<api-key>';
const ROOT_URL = `https://api.behance.net/v2/users?client_id=${API_KEY}`;
export const FETCH_USER = 'FETCH_USER';
export function fetchUser(users) {
$.ajax({
url: `${ROOT_URL}&q=${users}`,
type: 'get',
data: { users: {} },
dataType: 'jsonp'
})
.done(response => {})
.fail(error => {
console.log('Ajax request fails');
console.log(error);
});
return {
type: FETCH_USER
};
}
And sure enough, no more CORS error and I get back the users data in my Network > Preview tabs. Not very elegant, but sometimes you are just trying to solve a problem and at wits end.

Data fetching with React Native + Redux not working

I am building my first React Native app and use Redux for the data flow inside my app.
I want to load some data from my Parse backend and display it on a ListView. My only issues at the moment is that for some reason, the request that I create using fetch() for some reason isn't actually fired. I went through the documentation and examples in the Redux docs and also read this really nice blog post. They essentially do what I am trying to achieve, but I don't know where my implementation differs from their code samples.
Here is what I have setup at the moment (shortened to show only relevant parts):
OverviewRootComponent.js
class OverviewRootComponent extends Component {
componentDidMount() {
const { dispatch } = this.props
dispatch( fetchOrganizations() )
}
}
Actions.js
export const fetchOrganizations = () => {
console.log('Actions - fetchOrganizations');
return (dispatch) => {
console.log('Actions - return promise');
return
fetch('https://api.parse.com/1/classes/Organization', {
method: 'GET',
headers: {
'X-Parse-Application-Id': 'xxx',
'X-Parse-REST-API-Key': 'xxx',
}
})
.then( (response) => {
console.log('fetchOrganizations - did receive response: ', response)
response.text()
})
.then( (responseText) => {
console.log('fetchOrganizations - received response, now dispatch: ', responseText);
dispatch( receiveOrganizations(responseText) )
})
.catch( (error) => {
console.warn(error)
})
}
}
When I am calling dispatch( fetchOrganizations() ) like this, I do see the logs until Actions - return promise, but it doesn't seem to actually to fire off the request. I'm not really sure how how I can further debug this or what resources to consult that help me solve this issue.
I'm assuming that Redux is expecting a Promise rather than a function.. Is that true?
If so, I think your return function may not be working.
You have a new line after your return, and it's possible JavaScript is (helpfully) inserting a semicolon there.
See here: Why doesn't a Javascript return statement work when the return value is on a new line?

Resources