undefined is not an object (evaluating 'this.props.navigation') with fetch - javascript

I try my best to make a Sign-in form with React native but :
I can't make a redirection to 'App', the error message is : (TypeError: undefined is not an object (evaluating 'this.props.navigation')])
try {
fetch('http://93.xxx.xx.xx:5151/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: formBody,
})
.then(function(response) {
if (response.ok) {
this.props.navigation.navigate('App');
} else {
throw new Error("Failed to fetch [error : " + response.status + "]");
Alert.alert("Error [" + response.status + "] - " + response.statusText);
}
})
.then(function(response) {
if (response.ok) {
Alert.alert(response.userToken);
console.log(response);
} else {
Alert.alert("Error [" + response.status + "] - " + response.statusText);
}
})
} catch (error) {
if (error) {
console.error(error);
}
}
Is anyone know how to do that ?
I only have one solution so far :
fetch('http://93.xxx.xx.xx:5151/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: formBody
})
.then(res => {
if (res.ok) {
this.props.navigation.navigate('App')
} else {
if (res.status == 400) {
Alert.alert("Error 400 : login rejected because -> " + res.message)
} else {
throw Error(`Request rejected with status ${res.status}`);
}
}
})
.catch(console.error)
}
But with this solution i don't know how to save the User token ...

Its a scoping issue , change this :
.then(function(response) { // due to this you are losing `this` scope inside it
// due to that, this is not accessible, in result you will get error
this.props.navigation.navigate('App');
To :
.then((response) => { // <--- Fat arrow is what you need

The first one is a scope issue. If you want to use "this" in an anonymous function, you need to bind it to your object.
There are two ways to do that:
1) If you use the old function style, the former object don't get automatically bound to it. Therefore you need to bind the parent object manually.
If you want a little more explanation about it, look here: What is the use of the JavaScript 'bind' method?
Promise.then(function(res) {
return "Your return"
}.bind(this));
2) The second way is to use the ES6 "Fat arrow"-Function. This works internally a bit different and binds the content of the parent Obejct directly.
Promise.then(res => "Your return");
On your second Problem I don't fully understand what your goal is. Do you want the user token in your next route? If so, you should use "setParams":
fetch('http://93.xxx.xx.xx:5151/login', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
body: formBody
})
.then(res => {
if(res.ok) {
this.props.setParams({
token: res.userToken
})
this.props.navigation.navigate('App')
} else {
if (res.status == 400) {
Alert.alert("Error 400 : login rejected because -> " + res.message)
} else {
throw Error(`Request rejected with status ${res.status}`);
}
}})
.catch(console.error)
}}

Related

How to solve this JSON promise error with ReactJS?

I am new to React so there might be a lot of mistakes around. Also, I know there are similar questions, but none helped with my problem so far.
I'm working on a project (using a template) that is using Java Spring (back-end) and ReactJS (front-end).
At the moment I am trying to make a login form, and verify the credentials.
These are the functions that I use when I press Login:
onSubmit(){
let login = {
username: this.state.username,
password: this.state.password
};
this.checkLogin(login);
}
checkLogin(login){
return this.sendRequest(login, (result, status) => {
console.log("AICI NU AJUNG CRED");
if (result !== null && (status === 200 || status === 201)) {
console.log("Successfully inserted person with id: " + result);
this.reloadHandler();
} else {
console.log("There was an error " + result);
}
});
}
sendRequest(login, callback){
let request = new Request(HOST.backend_api + endpoint.login + "/login", {
method: 'POST',
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(login)
});
console.log(request.url);
console.log(login);
RestApiClient.performRequest(request, callback);
}
function performRequest(request, callback){
fetch(request)
.then(
function(response) {
if (response.ok) {
response.json().then(json => callback(json, response.status,null));
}
else {
response.json().then(err => callback(null, response.status, err));
}
})
.catch(function (err) {
//catch any other unexpected error, and set custom code for error = 1
callback(null, 1, err)
});
}
I try it with a "test" username and "test" password I have in my database.
And this is the result:
dev console result 1
dev console result 2

React native : What is the way to take data from 1 function and pass it to function number 2?

What is the way to take data from getUserConnectRequestData function and pass it to getUserConnectResponseData function ?
as you can see so i try to use then and responseData to for save the data of the getUserConnectRequestData function and than i try pass it into the getUserConnectResponseData function but itd not works .
getUserConnectRequestData().then(() => {
responseData();
});
and this is getUserConnectResponseData function that i want to pass the data from getUserConnectRequestData
export const getUserConnectResponseData = (responseData) => {
return new Promise((resolve, reject) => {
// console.log('THIS IS MY RESPONSE ==============>>>>>>>>>>>', responseData);
try {
fetch(
'https://hghghgghghg3223223',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
}
)
.then((response) => response.json())
.then((jsonResponse) => {
resolve(jsonResponse);
});
} catch (error) {
reject(error);
}
});
};
You need to accept the parameter and use it, and call the right function:
getUserConnectRequestData().then((responseData) => {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^
getUserConnectResponseData(responseData);
// −^^^^^^^^^^^^^^^^^^^^^^^^^^−^^^^^^^^^^^^
});
But since getUserConnectResponseData takes just that one parameter you know that the then callback will only be called with that one single argument:
getUserConnectRequestData().then(getUserConnectResponseData);
You also need to handle errors, so:
getUserConnectRequestData()
.then(getUserConnectResponseData)
.catch(error => {
// Handle/report error
});
There are a couple of other things to point out, though:
getUserConnectRequestData is falling prey to a promise anti-pattern: You don't need new Promise when you already have a promise (from fetch) to use.
You need to check for HTTP success before calling .json() on the response. Sadly, fetch only rejects on network errors, not HTTP errors.
Here's an updated version of getUserConnectRequestData:
export const getUserConnectResponseData = (responseData) => {
return fetch('https://hghghgghghg3223223', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
};
Because of that need for the check, I never use fetch directly, I have wrappers to do the check so I don't have to code it Every Single Time.
// General purpose
function fetchGeneral(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response;
});
}
// JSON
function fetchJSON(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
}
Those reject on both network and HTTP errors.

Is this a bad way to use ternary if else with promises in React

I am using ternary if else for calling different APIs(API calls are stored in another file).
(this.props.item === "burger" ? burgerObj.GetBurger (this.props.burger_id) : pizzaObj.GetPizza (this.props.pizza_id)) // burgerObj and pizzaObj are objects of their respective classes
.then((response) => {
if(response.status === 400) {
console.log ("Bad Request")
}
else {
response.json()
.then((findresponse) => {
this.prepareFood(findresponse) // Some function call
})
}
})
.catch ((e) => {
console.log (e)
})
}
// API calls in different classes. Same is for GetPizza
GetBurger(burger_id) {
return fetch(url + 'burgerId' + burger_id , {
headers: {
'Authorization': 'Bearer ' + authorization,
'Content-Type': 'application/json',
}
})
}
Is this a wrong way of handling such calls. Because when I'm using this API calling is working fine but the code never reaches promise (.then((response) => { - line no. 2). If I check Network I can see the response returned from API. Sorry if I am wrong somewhere I'm still new to React. Please help me.
Using Ternary is ok. But you can you can use Async/Await to clean up callbacks -
const { item, burger_id, pizza_id} = this.props;
function fetchWrapper(url) {
fetch(url, {
headers: {
Authorization: `Bearer${authorization}`,
'Content-Type': 'application/json',
}
})
.then(data => { return { data, error: null }; })
.catch(error => { return { error, data: null }; });
}
// API calls in different classes. Same is for GetPizza
async function GetBurgerAsync(burgerId) {
let response = await fetchWrapper(`url burgerId${burgerId}`);
const data = await response.json();
return data;
}
const foodType = item === 'burger' ? GetBurgerAsync(burger_id) : GetPizzaAsync(pizza_id);
if (foodType.data) {
this.prepareFood(foodType);
}
Edit: refactored, wrapping fetch as only url should change on every call. Token should be same across all calls.

How to wrap JavaScript fetch in a function - unhandled promise rejection

I'm trying to write a wrapper function for the JavaScript fetch command.
I took the example code from this post:
function fetchAPI(url, data, method = 'POST') {
const headers = {
'Authorization': `Token ${getAuthToken()}`,
};
return fetch(url, { headers, 'method': method, 'body': data })
.then(response => {
if (response.ok) {
const contentType = response.headers.get('Content-Type') || '';
if (contentType.includes('application/json')) {
return response.json().catch(error => {
return Promise.reject(new Error('Invalid JSON: ' + error.message));
});
}
if (contentType.includes('text/html')) {
return response.text().then(html => {
return {
'page_type': 'generic',
'html': html
};
}).catch(error => {
return Promise.reject(new Error('HTML error: ' + error.message));
});
}
return Promise.reject(new Error('Invalid content type: ' + contentType));
}
if (response.status === 404) {
return Promise.reject(new Error('Page not found: ' + url));
}
return response.json().then(res => {
// if the response is ok but the server rejected the request, e.g. because of a wrong password, we want to display the reason
// the information is contained in the json()
// there may be more than one error
let errors = [];
Object.keys(res).forEach((key) => {
errors.push(`${key}: ${res[key]}`);
});
return Promise.reject(new Error(errors)
);
});
}).catch(error => {
return Promise.reject(new Error(error.message));
});
};
And I'm calling it like this:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
});
Edit: I have modified the code to display information returned by the server if the request is ok but refused, for example because of an invalid password. You have to interrogate the response json if ok == false.
A valid URL fetch is fine. But if there is an error, I see an Unhandled Rejection (Error): error message.
Why is it that the rejects are unhandled even though they are in catch blocks? What's the secret sauce here?
The way to avoid an unhandled promise rejection, is to handle it:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
}).catch(error => {
// do something meaningful here.
});;

How to get Readable error response from JavaScript Fetch API?

I am working on Reactjs redux on front-end and Rails API as a back-end.
So now I call API with Fetch API method but the problem is I cannot get readable error message like what I got inside the network tabs
this is my function
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
});
}
}
But when errors came I cannot figure out how to get readable response message like I got when I check on my browser network tabs
So this is what I got from the network tabs when I got errors.
My console
This is my rails code
def create
user = User.new(user_params)
if user.save
#UserMailer.account_activation(user).deliver_now
render json: user, status: 201
else
render json: { errors: user.errors }, status: 422
end
end
But I cannot find out how can I get that inside my function
Since the text is hidden inside promise within response object, it needs to be handled like a promise to see it.
fetch(bla)
.then(res => {
if(!res.ok) {
return res.text().then(text => { throw new Error(text) })
}
else {
return res.json();
}
})
.catch(err => {
console.log('caught it!',err);
});
Similar to your answer, but with a bit more explanation... I first check if the response is ok, and then generate the error from the response.text() only for the cases that we have a successful response. Thus, network errors (which are not ok) would still generate their own error without being converted to text. Then those errors are caught in the downstream catch.
Here is my solution - I pulled the core fetch function into a wrapper function:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Then when I use it, I define how to handle my response and errors as needed at that time:
fetchJSON(url, options)
.then((json) => {
// do things with the response, like setting state:
this.setState({ something: json })
})
.catch(error => {
// do things with the error, like logging them:
console.error(error)
})
even though this is a bit old question I'm going to chime in.
In the comments above there was this answer:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Sure, you can use it, but there is one important thing to bare in mind. If you return json from the rest api looking as {error: 'Something went wrong'}, the code return res.text().then(text => {throw new Error(text)}) displayed above will certainly work, but the res.text() actually returns the string. Yeah, you guessed it! Not only will the string contain the value but also the key merged together! This leaves you with nothing but to separate it somehow. Yuck!
Therefore, I propose a different solution.
fetch(`backend.com/login`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
.then(response => { ...someAdditional code })
.catch(error => reject(error.message))
So let's break the code, the first then in particular.
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
If the response is okay (i.e. the server returns 2xx response), it returns another promise response.json() which is processed subsequently in the next then block.
Otherwise, I will AGAIN invoke response.json() method, but will also provide it with its own then block of code. There I will throw a new error. In this case, the response in the brackets throw new Error(response.error) is a standard javascript object and therefore I'll take the error from it.
As you can see, there is also the catch block of code at the very end, where you process the newly thrown error. (error.message <-- the error is an object consisting of many fields such as name or message. I am not using name in this particular instance. You are bound to have this knowledge anyway)
Tadaaa! Hope it helps!
I've been looking around this problem and has come across this post so thought that my answer would benefit someone in the future.
Have a lovely day!
Marek
If you came to this question while trying to find the issue because response.json() throws "Unexpected token at position..." and you can't find the issue with the JSON, then you can try this, basically getting the text and then parsing it
fetch(URL)
.then(async (response) => {
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
// Here first we convert the body to text
const text = await response.text()
// You can add a console.log(text), to see the response
// Return the JSON
return JSON.parse(text)
})
.catch((error) => console.log('Error:', error))
.then((response) => console.log(response))
I think you need to do something like this
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
return response.json();
})
.then(function(object){
if (object.errors) {
dispatch(update_errors(response));
throw new Error(object.errors);
} else {
console.log("succeed json re");
dispatch(update_user(json));
}
})
.catch(function(error){
this.setState({ error })
})
}
}
You can access the error message with this way:
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
})
// here's the way to access the error message
.catch(function(error) {
console.log(error.response.data.message)
})
;
The best choice is not to catch the error in the fetch because this will be useless:
Just in your api put a response with not code error
static GetInvoicesAllData = async (req,res) =>
{
try{
let pool = await new Connection().GetConnection()
let invoiceRepository = new InvoiceRepository(pool);
let result = await invoiceRepository.GetInvoicesAllData();
res.json(result.recordset);
}catch(error){
res.send(error);
}
}
Then you just catch the error like this to show the message in front end.
fetch(process.env.REACT_APP_NodeAPI+'/Invoices/AllData')
.then(respuesta=>respuesta.json())
.then((datosRespuesta)=>{
if(datosRespuesta.originalError== undefined)
{
this.setState({datosCargados:true, facturas:datosRespuesta})
}
else{ alert("Error: " + datosRespuesta.originalError.info.message ) }
})
With this you will get what you want.
You variables coming back are not in response.body or response.message.
You need to check for the errors attribute on the response object.
if(response.errors) {
console.error(response.errors)
}
Check here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You should actually be returning an error response code from the server and use the .catch() function of the fetch API
First you need to call json method on your response.
An example:
fetch(`${API_URL}`, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfoParams)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => {
console.log("error", err)
});
Let me know the console log if it didn't work for you.

Categories