How to chain 2 API calls with redux thunk? - reactjs

I'm making a weather app, which detects your current location's coordinates by making an API call to freegeoip.com, then takes those coordinates to make an API call to openweathermap.org to fetch your current location's weather.
How would I do this with redux thunk?
This is my current action creator code:
import axios from 'axios';
export const FETCH_CURRENT_CITY = 'FETCH_CURRENT_CITY';
const API_KEY = '95108d63b7f0cf597d80c6d17c8010e0';
const ROOT_URL = `http://api.openweathermap.org/data/2.5/weather?appid=${API_KEY}`;
export function fetchCurrentCityCoords() {
const request = axios.get('http://freegeoip.net/json/');
console.log('Request coords:', request);
return (dispatch) => {
request.then(({response}) => {
dispatch(fetchCurrentCityWeather(response));
});
};
}
export function fetchCurrentCityWeather(coords) {
const lat = coords.data.latitude;
const lon = coords.data.longitude;
const url = `${ROOT_URL}&lat=${lat}&lon=${lon}`;
const request = axios.get(url);
console.log('Request weather:', request);
return (dispatch) => {
request.then(({response}) => {
dispatch({
type: FETCH_CURRENT_CITY,
payload: response
})
});
};
}
Console logs:
Request coords: Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Uncaught (in promise) TypeError: Cannot read property 'latitude' of undefined
at fetchCurrentCityWeather (bundle.js:26131)
at bundle.js:26125

Are you sure the API response holds a data property?
try debugging the first line of fetchCurrentCityWeather function and check what coords holds, or just try console.log(coords); to see it's object structure.
maybe this:
const lat = coords.data.latitude;
const lon = coords.data.longitude;
should be like that:
const lat = coords.latitude;
const lon = coords.longitude;
Edit
following your comment,
for debug purpose try editing your fetchCurrentCityWeather function to this code and see what is printed to the console:
export function fetchCurrentCityWeather(coords) {
console.log(coords);
//const lat = coords.data.latitude;
//const lon = coords.data.longitude;
//const url = `${ROOT_URL}&lat=${lat}&lon=${lon}`;
//const request = axios.get(url);
//console.log('Request weather:', request);
//return (dispatch) => {
//request.then(({response}) => {
// dispatch({
//type: FETCH_CURRENT_CITY,
// payload: response
//})
//});
// };
}
EDIT #2
following your comment, you are getting undefined hence you can't use .map on it. this is your answer to the error.
As for why you are getting undefined, i think it's because this block of code:
return (dispatch) => {
request.then(({response}) => {
dispatch(fetchCurrentCityWeather(response));
});
};
You are doing "Destructuring" to the response parameter with this {response}.
try this instead:
return (dispatch) => {
request.then((response) => {
dispatch(fetchCurrentCityWeather(response));
});
};

Uncaught (in promise) TypeError: Cannot read property 'latitude' of undefined
at fetchCurrentCityWeather (bundle.js:26131)
at bundle.js:26125
Problems appears to be your const lat = coords.data.latitude it's undefined.
So you have to check if the
return (dispatch) => {
request.then(({response}) => {
dispatch(fetchCurrentCityWeather(response));
});
CallsfetchCurrentCityWeather with correct response object you expect to have properties data.latitude & data.longitude you seem to incorrectly refer.
Advise.
console.log(coords); inside fetchCurrentCityWeather function to see if it has those properties.

Related

Parse data through API with useEffect for Algolia

I need to parse stock data through YahooFinance Stocks API, using RapidAPI here how it would look like as an example response.
https://rapidapi.com/integraatio/api/yahoofinance-stocks1/
The error I am getting is:
Uncaught (in promise) ReferenceError: Cannot access 'data' before
initialization
"results":1294 items
[100 items
0:{4 items
"exchangeCode":"NMS"
"symbol":"1"
"companyName":"1"
"industryOrCategory":"N/A"
}
1:{...}4 items
2:{4 items
"exchangeCode":"NMS"
"symbol":"AAON"
"companyName":"AAON, Inc."
"industryOrCategory":"Industrials"
}
3:{4 items
"exchangeCode":"NMS"
"symbol":"AAPL"
"companyName":"Apple Inc."
"industryOrCategory":"Technology"
}
]
useEffect(() => {
const config = {
headers: {
"x-rapidapi-host": "stock-market-data.p.rapidapi.com",
"x-rapidapi-key": APIKEY,
},
};
const fetchStocks = async () => {
const json = JSON.parse(data);
const results = Object.keys(json["results"]);
const stockInfo = results.map(
(result) =>
(result = {
result,
close: String(json["results"][result]),
})
);
const { data } = await axios.get(
"https://stock-market-data.p.rapidapi.com/market/exchange/nasdaq",
config
);
data.forEach((results) => {
results.objectID = results.length;
});
setStocks(data);
};
fetchStocks();
}, [])
This seems like a marshaling issue in your Javascript more than an Algolia issue. Perhaps because the await axios.get() needs to be in an async function per https://stackabuse.com/making-asynchronous-http-requests-in-javascript-with-axios/
To use the async/await syntax, we need to wrap the axios.get() function call within an async function
Curious the contents of data before and after the data.forEach() (also could that be a map?)

how does swr or useSWR works properly in reactjs / nextjs?

In the fisrt part I ma suign fetch() and it is working fine but whe i use useSWR() it returns UNDEFINED
export const getAllEvents = async()=>{
const response = await fetch('https://*******-rtdb.firebaseio.com/events.json');
const data = await response.json();
const events = [];
for (const key in data){
events.push({
id:key,
...data[key]
});
}
return events; // returns data as I wanted.. perfect
}
but in the following snippet it returns undefined (the same url)
import useSWR from 'swr';
const {data, error} = useSWR('https://*******-rtdb.firebaseio.com/events.json');
console.log(data); // returns undefined
useSWR expects fetcher function as second argument:
https://swr.vercel.app/#overview
It can be a custom method e.g.
export const fetcher = async (...args: Parameters<typeof fetch>) => {
const res = await fetch(...args);
if (!res.ok) {
throw { status: res.status, statusText: res.statusText };
}
return res.json();
};
Use swr also needs to take a fetcher function, right now you're just passing down a url. Swr doesnt know what to do with that url..
const { data, error } = useSWR(
"https://api.github.com/repos/vercel/swr",
fetcher
);
const fetcher = (url) => fetch(url).then((res) => res.json());
I suggest you read the docs.
add this :
const fetcher = (...args) => fetch(...args).then(res => res.json())
const {data, error} = useSWR('https://*******-rtdb.firebaseio.com/events.json', fetcher);

React - how to make an api request wait for the value of an undefined variable when submitting a form

I'm trying to make an api request by passing an id parameter in the request url
async function carregarPlanoCurso() {
const response = await api.get(`cursos/${resPesquisaCurso.id}/plano`);
return response.data;
}
The problem is that when calling the carregarPlanoCurso in english "loadCoursePlan()" method the variable resPesquisaCurso "searchCoursesearch" is undefined and I get the following error:
GET http://localhost:3001/cursos/undefined/plano 500 (Internal Server Error)
createError.js:16 Uncaught (in promise) Error: Request failed with status code 500
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
The variable resPesquisaCurso depends on the carregarCursos() method which loads all courses which are then filtered according to id:
const [resPesquisaDisciplina, setResPesquisaDisciplina] = useState([]);
const [resPesquisaCurso, setResPesquisaCurso] = useState([]);
const [resPesquisaPlano, setResPesquisaPlano] = useState([]);
const [resPesquisaEdicao, setResPesquisaEdicao] = useState([]);
const [plan, setPlan] = useState([]);
const user = useSelector((state) => state.user.profile);
async function carregarDisciplinas() {
const response = await api.get('disciplinas/');
return response.data;
}
async function carregarCursos() {
const response = await api.get('cursos/');
return response.data;
}
async function carregarEdicoes() {
const response = await api.get('disciplinas/all/edicoes');
return response.data;
}
async function carregarPlanoCurso() {
const response = await api.get(`cursos/${resPesquisaCurso.id}/plano`);
return response.data;
}
function handleSubmit({
anoLetivo1,
curso1,
unidadeCurricular1,
dataInicio1,
dataFinal1,
}) {
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
});
carregarDisciplinas().then((value) => {
setResPesquisaDisciplina(
value.filter((disciplina) => disciplina.nome === unidadeCurricular1)
);
});
carregarPlanoCurso().then((value) => {
setPlan(value);
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
});
carregarEdicoes().then((value) => {
setResPesquisaEdicao(
value.filter(
(edicao) => edicao.id_disciplina === setResPesquisaDisciplina.id
)
);
});
console.log(resPesquisaPlano);
}
My problem is how to make the method carregarPlanoCurso() wait for the execution of the carregarCursos() method to finish executing for only then I can make sure that the searchCourse search variable is not undefined and make the call to api passing this variable as a parameter in the url:
Problem focus:
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
});
carregarPlanoCurso().then((value) => {
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
});
Maybe you can do this way:
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
}).then(() => carregarPlanoCurso().then((value) => {
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
}));
When you use a promise, JavaScript will not wait for that promise to resolve or reject. It will put this call in another part of the event loop and continue executing your code.
If I could, I want to suggest a change inside your handleSubmit function like this:
function handleSubmit({
anoLetivo1,
curso1,
unidadeCurricular1,
dataInicio1,
dataFinal1,
}) {
try {
const {data: cursos} = await carregarCursos();
setResPesquisaCurso(cursos.filter((curso) => curso.nome === curso1));
const {data: disciplinas} = await carregarDisciplinas();
setResPesquisaDisciplina(disciplinas.filter((disciplina) => disciplina.nome === unidadeCurricular1);
// rest of your code using this approach.
} catch (err) {
// handle with your error
}
}
Doing this way JavaScript will wait for the promise resolves or reject.

mocking my fetch function does not work, keeps getting back undefined

I am trying to mock a simple function that uses fetch. The function in question looks like this:
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
//getPokemon.js
const randomId = () => Math.floor(Math.random() * 151 + 1);
const pokemonApiUrl = `https://pokeapi.co/api/v2/pokemon/`;
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
const id = randomId();
let pokemon = { name: "", image: "" };
try {
const result = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
console.log(result)
const data = await result.json();
pokemon.name = data.name;
pokemon.image = data.sprites.other["official-artwork"].front_default;
return pokemon;
} catch (err) {
console.error(err);
Whenever I try to mock the function in my unit tests I receive back a TypeError: Cannot read property 'json' of undefined. Basically, the result comes back as undefined and thus we cannot call our .json(). It works fine in production and the fetch calls work as expected. I am using React Testing Library and Jest.
I have tried to replaced the global fetch in the following manner:
//PokemonPage.test.js
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: { name: 'Charizard' } }),
})
);
I've also tried to create a fakeFetch and send it in to my function as a dependency injection, but I get the exact same error.
Lastly, I've tried to install jest-fetch-mock but yet again I am getting the same error... Has anyone encountered the same thing?
The failing function gets called in production here:
function Pokemon({ pokemonTrainer }) {
...
useEffect(() => {
async function fetchData() {
pokemonRef.current = await getPokemon();
setPokemonList((prev) => [
...prev,
{ name: pokemonRef.current.name, image: pokemonRef.current.image },
]);
}
fetchData();
}, []);
...
}

Axios Error Networ error on request Google place api

im trying to make a request to google api but returns me network error. If i put the url in the browser, brings me the information correctly.I tryed to formate the request without success. The google places search works correctly too.
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey`;
const {data} = await axios.get(url)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
and
export const fetch_information = (skip, limit, filter) => async (dispatch) => {
try {
var url = `https://maps.googleapis.com/maps/api/place/details/json?`;
let config = {
params: {
place_id: 'ChIJk0aJYPbk3JQRLpKN20Jecko',
key: 'myKey',
},
}
const {data} = await axios.get(url, config)
console.log(data)
} catch (error) {
console.log(error.message)
}
}
I think that the request looks a bit messy. I'm under the impression that you are trying to pass results to a redux store. Let's see if we can clean this up a bit.
export const fetch_information = async () => dispatch => {
const req = await axios.get("https://maps.googleapis.com/maps/api/place/details/json?place_id=ChIJk0aJYPbk3JQRLpKN20Jecko&fields=name,rating,formatted_phone_number&key=MyKey");
const data = await req.json();
return data;
//or, for your purpose...
console.log(data);
//can also dispatch for store
}
I didn't see anything you were passing as necessary for this.

Resources