When trying to resolve my promise, I get the error "Objects are not valid as a React child (found: [object Promise])."
In the console.log after my API request in GetCardsFromBoard(), the typeOf() my response is string and it prints out the data to the console. But in TrelloTester(), the console shows that my response is still Promise<pending> and I get the error.
I've tried so many configurations but I can't get my promise to resolve, thanks for any help!
const fetch = require('node-fetch');
export async function GetCardsFromBoard(board) {
let cards = await fetch(
baseURL+'boards/'+board+'/cards?key='+key+'&token='+token,
{
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(response => {return response.text()})
.catch(err => console.error(err));
console.log(typeof(cards), cards); //prints "string" type and then the entire json response
return cards;
}
export function TrelloTester() {
let bodyStr = GetCardsFromBoard(boardID);
console.log("resp: ", bodyStr); //shows Promise<pending>, but with the correct response value inside
return (
<BigCard header=" " body={bodyStr}/>
);
}
export default TrelloTester;
GetCardsFromBoard returns a Promise. async/await is just sugar on top of promises and doesn't actually make the code any less asynchronous. Therefore, your TrelloTester component will still have to treat the function as a Promise.
Note that I removed the then in your promises; if you're using async/await then you can just await all the promises!
export async function GetCardsFromBoard(board) {
try {
const cards = await fetch(
baseURL+'boards/'+board+'/cards?key='+key+'&token='+token,
{
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
return await cards.text();
} catch(e) {
console.log(e);
}
}
In you component, since this is asynchronous, I recommend treating it as an effect and using the useEffect hook in conjunction with the useState hook.
import React, { useEffect, useState } from "react";
export function TrelloTester() {
const [bodyStr, setBodyStr] = useState("");
useEffect(() => {
cont getCards = async () => {
const cards = await GetCardsFromBoard(boardID);
setBodyStr(cards);
}
getCards();
}, []);
return bodyStr ? (
<BigCard header=" " body={bodyStr}/>
) : "Loading...";
}
export default TrelloTester;
http response needs to be converted to json, use json() method.
let cards = await fetch(
baseURL+'boards/'+board+'/cards?key='+key+'&token='+token,
{
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(response => {return response.json()})
.catch(err => console.error(err));
Related
Here is the function that I wanna test, it takes a token and a description as props. Normally in React code, I can get token from useContext.
export const updateUserProfileAbout = async (
token,
description
) => {
const dataUpdateTemplateDescriptionRes = await patchData(`me/`, token, {
about:description,
});
const dataUpdateTemplateDescriptionJson = await dataUpdateTemplateDescriptionRes.json();
return dataUpdateTemplateDescriptionJson;
};
And here is my custom patchData function:
const patchData = async (urn, token, data = "") => {
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access}`,
};
const body = data ? JSON.stringify(data) : null;
let response;
if (body) {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
body,
});
} else {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
});
}
if (!response.ok) throw new Error(response.status);
return response;
};
You are right. You don't need the token. All you need to do for mocking the fetch is the following:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve()) as jest.Mock);
If you want to retrieve a specific object from a json response, you can use:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ myObject }) })) as jest.Mock);
You can also reject it to trigger the error catch:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
If you want to return something multiple times, change the mockImplementationOnce to whatever you need (maybe mockImplementation, for returning it every time you call it).
If you also want to expect the call of the fetch just add a constant:
const myFetch = jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
You can then expect it via: expect(myFetch).toBecalledTimes(1);
After one more day of researching, I might be wrong though but I don't think I have to care about token or authorization when unit testing for front-end. All I need is jest.fn() to mock function and jest.spyOn(global, "fetch") to track fetch API.
For more information, here are some references that I read:
https://codewithhugo.com/jest-fn-spyon-stub-mock/
https://dev.to/qmenoret/mocks-and-spies-with-jest-32gf
https://www.pluralsight.com/guides/how-does-jest.fn()-work
https://www.loupetestware.com/post/mocking-api-calls-with-jest
I'm calling my API through AXIOS. My response is coming back, which i can see if i check my network, but it returns empty data when I console.log:
useEffect(() => {
async function bt() {
const authAxios = axios.create({
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const Data = await authAxios
.get('/tasks/')
.then((res) => {
console.log(res);
})
.catch((e) => {
console.log(e);
});
}
bt();
}, []);
1 - You've used an async function alongside trying to resolve the promise. Either get rid of the async and await or get rid of everything after "await authAxios"(i.e the catch and then blocks)
2 - The reason why res.data console log is blank is because res.data is undefined try changing that line to just res. Check the response Object to see if the info is there.
.then((res) => {
//console.log(res.data);
console.log(res)
setmessage(res.data);
// this.getUserUpdate(res.data)
console.log(res.data);
})
For res.data to work you need to make sure that your API creates an attribute called data for the response. If this doesn't work I recommend you also add what your back-end looks like.
As #Pyro mentioned you are mixing up 2 completly different promise-handling techniques.
You'd have to decide to either use .then()🔗 or async/await🔗
e.g.
With async/await:
useEffect(() => {
async function bt() {
const authAxios = axios.create({
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const Data = await authAxios.get('/tasks/')
}
bt();
}, []);
Or with .then()
useEffect(() => {
function bt() {
const authAxios = axios.create({
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const Data = authAxios.get('/tasks/').then(console.log).catch(console.log);
}
bt();
}, []);
I'm trying to work with async await and fetching data using the fetch api
My problem is, I just don't quite understand how I really get the answer from the server and not the status of the promise. I get the following in the console
Promise {<pending>}
-------------------------------
{locale: "en"}
but what I rather expect is the server response which should be "locale: en".
my code:
const locale = getLocale();
console.log(locale) // here I want to have "locale: en" as a output
async function fetchLocale() {
return await fetch('/get-language', {
headers: {
'Accept': 'application/json'
},
method: 'GET',
}).then(response => {
if (response.ok) {
return response.json()
}
return Promise.reject(Error('error'))
}).catch(error => {
return Promise.reject(Error(error.message))
})
}
async function getLocale() {
return await fetchLocale();
}
The goal I want to archive is, to return this "locale: en" response and store it in the "locale" const at the beginning of my code example.
KR
Your function should look more like this:
async function getLocale() {
let response = await fetch('/get-language', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: 'GET',
});
// you can check for response.ok here, and literally just throw an error if you want
return await response.json();
}
And you can consume the result of that in an async function using await
const locale = await getLocale();
console.log(locale);
or by using the promise methods
getLocale().then(function(locale){
console.log(locale);
})
To get the value a Promise resolves with and not the promise itself, use .then() or await. Note that both require a callback or asynchronous context as it's simply not possible to bring this asynchronous result to a synchronous context such as the top level of your file.
getLocale().then(locale => {
console.log(locale);
//locale is only valid here
});
Into my actionCreators I call some rest API endpoints on action e.g. UPDATE_CART_ITEM etc.
At first I was using axios like so return axios()...:
export const login = (username, password) => (dispatch) => {
dispatch(requestLogin())
const URL = `${USERS_URL}?username=${username}&password=${password}`
return axios(URL)
.then((response) => {
return response.data
})
.then((user) => {
dispatch(loginSuccess(user))
// save the user on localStorage
localStorage.setItem('user', JSON.stringify(user))
// direct the logedin user to the games page
history.push('/')
})
.catch(() => {
return dispatch(loginFailure())
})
}
Now I use async/await like so:
// On payload i have the obj: {prId: 'xxxxx'}
export const updateCartItem = (payload) => async (dispatch) => {
const response = await fetch('cart/update',
{
body: JSON.stringif(payload),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
})
// I m not sure if i have to return the result
const result = await response.json()
// I dispatch the action with the payload
await dispatch(return {
payload,
type: UPDATE_CART_ITEM,
})
} catch (err) {
dispatch(cartActionFail(err))
}
}
So, inside the updateCartItem function, how should I handle the result ?
Since I'm passing payload to the reducer, it seems that I don't need it.
You probably want to do something like this:
dispatch({ payload: response, type: UPDATE_CART_ITEM })
dispatch(return { /*...*/ }) doesn't make sense as far as I know, and dispatch() doesn't return a promise so there's no point in awaiting on it.
In general, if you want to replace promise chains with async/await, then you want to replace foo.then(bar => { baz(bar); }) with const bar = await foo; baz(bar);
If you need to consume your result immediatly, then you should dispatch an action like UPDATE_CART_ITEM_SUCCEED, otherwise do nothing.
BTW, I recommend you to use redux-saga or redux-thunk to handle your app side effects such as API calls.
If you use the same payload for your action creator what happens if something goes wrong at your backend? Your backend side won't change but your state won't be aware of that and updates itself with payload. This is why you should use some error check here. Also, personally I use last result as a payload to my action creators, not the original payload.
export const updateCartItem = payload => async ( dispatch ) => {
try {
const response = await fetch(
"cart/update",
{
body: JSON.stringif( payload ),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
}
);
if ( !response.ok ) { throw new Error( "response error" ); }
const result = await response.json();
return dispatch( {
payload: result,
type: UPDATE_CART_ITEM,
} );
} catch ( error ) {
return dispatch( cartActionFail( error.message ) );
}
};
You can change and enhance this logic according to your needs. As #vkarpov15 pointed out dispatch does not use return explicitly and it does not return a promise, hence you don't need await there.
I need to return the result of a function from another page in react native which performing a fetch call. I use the method as follows. As I know this is because asynchronous call. Is there a special way to achieve this in react native ?
fetchcall.js
import address from '../actions/address'
const dashboard = {
getvals(){
return fetch(address.dashboardStats(),
{method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify( {...
}),
})
.then((response) => response.json())
.then((responseData) => {
console.warn(responseData);
return responseData;
})
.catch((error) => { console.warn(error); })
.done();
// return 'test_val'';
}
}
export default dashboard;
dashboard.js
import dashboard from '../../services/dashboard';
class Dashboard extends Component {
componentDidMount(){
console.warn(dashboard.getvals());
}
}
export default connect(mapStateToProps, bindAction)(Dashboard);
Its display the result as "undefined", but that fetch call works and it displays the result. Any suggestion?
In fetchcall.js you are returning a Promise. Also since you are returning the responseData in the .then() method itself, you don't need the .done() method.
Since getvals() is returning a Promise, you need to access it's value in a .then() method.
Overall, your code should be like this:
function getvals(){
return fetch('https://jsonplaceholder.typicode.com/posts',
{
method: "GET",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseData) => {
console.log(responseData);
return responseData;
})
.catch(error => console.warn(error));
}
getvals().then(response => console.log(response));
The best architectural pattern, I think, is to use a callback function, usually by writing in as an anonymous function.
///Component.js
my_service.login((data=>{
this.setState({body: data.body});
}));
////Service.js
export const login = function (cb){
fetch('http://myapi.com/103?_format=hal_json')
.then((response) =>{
return response.json();
})
.then((data) =>{
cb(data);
});
}
I am still a junior developer, but use this pattern frequently. If someone has a reason for a different approach, I would love to hear it.
fetch always return a Promise doesn't matter what you are returning.
so you can return a string, variable or something else but you have to use .then to access data
file where you make fetch request
export const graphql = () => {
return fetch("https://graphqlzero.almansi.me/api", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: `{
user(id: 1) {
id
name
}
}`
})
})
.then((res) => res.json()).then((responseData) => {
console.log(responseData);
return responseData;
})
}
file where you call function
graphql()
.then((e) => {
console.log("data", e)
});