How to implement a callback function in React Native? - javascript

I'm just wondering how to implement callback function in onPress function. I want to make sure the first function completed then the second process triggered.
onPress={() => {
onSignIn(); //run this function first
if(_values.success == 1){ //then run this after onSignIn() function completed
navigation.navigate("SignedIn");
}
}}
onSignIn()
export const onSignIn = async () => {
if (_values.username && _values.password) {
fetch("http://localhost:3001/sessions/create", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: _values.username,
password: _values.password
})
})
.then((response) => response.json())
.then((responseData) => {
if(responseData.access_token){
AsyncStorage.setItem(USER_KEY, responseData.access_token);
_values.success = 1;
alert("Login successfully!");
return _values.success;
} else {
alert("Wrong username and password!");
_values.success = 0;
return _values.success;
}
})
.done();
} else{
alert("Please enter your username and password.");
}
}
Reference: https://github.com/datomnurdin/auth-reactnative

Return a Promise from the method and add .then() to the function call
export const onSignIn = async () => {
const promise = new Promise(async (resolve, reject) => {
try {
//do something and return result on success
resolve(result);
}catch(msg) { reject(msg);}
});
return promise;
}
Call the method like this:
onSignIn ().then(
(response,error) => {
//get callback here
});

By "wait for onSignIn to complete" I guess you mean it is an asynchronous function, you can then use the await operator to wait for it to be over
onPress={() => {
await onSignIn(); //run this function first
if(_values.success == 1){ //then run this after onSignIn() function completed
navigation.navigate("SignedIn");
}
}}
Then you will ahve to add async to your onSignIn function :
onSignIn = async () => {console.log("signing in")}
Here is a more 'React-y' way too handle your full process :
import React, { Component } from 'react'
export default class Example extends Component {
onSignIn = async () => {
console.log('singing in....')
}
pressEvent = async event => {
await this.onSignIn(); //run this function first
if (_values.success == 1) { //then run this after onSignIn() function completed
navigation.navigate("SignedIn");
}
}
render() {
return (
<div onPress={this.pressEvent}>
</div>
)
}
}
EDIT :
Instead of returning a 0 or a 1, you could simply return a boolean in your function. Also, now that you are using the more modern async/await operators, you can remove the then functions :
export const onSignIn = async () => {
if (_values.username && _values.password) {
const response = await fetch("http://localhost:3001/sessions/create", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: _values.username,
password: _values.password
})
})
const responseData = JSON.parse(response)
if (responseData.access_token) {
AsyncStorage.setItem(USER_KEY, responseData.access_token);
alert("Login successfully!");
return true
} else {
alert("Wrong username and password!");
return false
}
} else {
alert("Please enter your username and password.");
}
}
Now, here is how you will fetch the response from this function :
export default class Example extends Component {
pressEvent = async event => {
if (await onSignIn()) navigation.navigate("SignedIn");
}
render() {
return (
<div onPress={this.pressEvent}>
</div>
)
}
}

This code should help you
async onPressFunc() {
await onSignIn(); //run this function first
if(_values.success == 1){ //then run this after onSignIn() function completed
navigation.navigate("SignedIn");
}
}
onPress={this.onPressFunc}

first, onSign() must be an async function, don't add the done() function,you want to handle it latter:
export const onSignIn = async () => {
if (_values.username && _values.password) {
fetch("http://localhost:3001/sessions/create", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: _values.username,
password: _values.password
})
})
.then((response) => response.json())
.then((responseData) => {
if(responseData.access_token){
AsyncStorage.setItem(USER_KEY, responseData.access_token);
_values.success = 1;
alert("Login successfully!");
return _values.success;
} else {
alert("Wrong username and password!");
_values.success = 0;
return _values.success;
}
})
} else{
throw "Please enter your username and password.";
}
}
then, you just do:
onPress={() => {
onSignIn().then( (values) => {
if(values.success == 1){
navigation.navigate("SignedIn");
}
})
.catch(error => console.log(error)) //do something in case onSignIn fails
}}

Related

How to throw a server error when fetching JS

I'm new in JavaScript and i have a task to post an email input from form to a node server,everything works fine,but i should implement this functionality:
When an email is forbidden#gmail.com, the server responds with the 422 status code and payload which contains the information about the error. Use browser developer tools to examine the response for this scenario. Display the error message in the browser using window.alert().
I created a customException,it gives me the error in the console,but the server still responds with the 200 status code,but as i understand,it should give an error and the post should not work.How to do this task,i have no idea..?
Fetch functions:
import { validateEmail } from './email-validator.js'
export const sendSubscribe = (emailInput) => {
const isValidEmail = validateEmail(emailInput)
if (isValidEmail === true) {
sendData(emailInput);
// if (emailInput === 'forbidden#gmail.com'){
// throw new CustomException('422');
// }
}
}
const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data ? {
'Content-Type': 'application/json'
} : {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!');
error.data = errResData;
throw error;
});
}
return response.json();
});
};
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
console.log(responseData);
}).catch(err => {
console.log(err, err.data);
});
}
function CustomException(message) {
const error = new Error(message);
error.code = "422";
window.alert('Forbidden email,please change it!')
return error;
}
CustomException.prototype = Object.create(Error.prototype);
Validate function:
const VALID_EMAIL_ENDINGS = ['gmail.com', 'outlook.com', 'yandex.ru']
export const validateEmail = (email) => !!VALID_EMAIL_ENDINGS.some(v => email.includes(v))
export { VALID_EMAIL_ENDINGS as validEnding }
Please help.Thanks in advance!
Something like this should work:
Server code:
Simplify validate function.
export const isValid = (email) => {
if (email === 'forbidden#gmail.com') {
return false
}
return true
}
Then on your route, something like this, assuming expressjs behind.
app.post('/subscribe', (req, res, next) => {
const email = req.body.email
if (!isValid(email)) {
return res.status(433).send('Email is forbidden')
}
return res.status(200).send('Success')
})
In your frontend you can just post to /subscribe with the email payload
const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data ? {
'Content-Type': 'application/json'
} : {}
})
.then(response => response.json())
};
And in your sendData you can catch the error, like you're doing
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
console.log(responseData);
}).catch(err => {
console.log(err); // Email is forbidden
window.alert('Boo!')
});
}
Sidenote: In most cases prototyping should be avoided in javascript.

Service call is not going in react-native. Getting warning like "Possible unhandled Promise Rejection, Reference error: response is not defined"

I am new to react native and making service call for the first time. My problem is service call is not going and getting warning like
Possible unhandled Promise Rejection, Reference error: response is not defined.
I am trying to hit loginUser function.
Api.js
const BASE_URL = "http://localhost:8200";
export const api = async (url, method, body = null, headers = {}) => {
try {
const endPoint = BASE_URL.concat(url);
const reqBody = body ? JSON.stringify(body) : null;
const fetchParams = {method, headers};
if((method === "POST" || method === "PUT") && !reqBody) {
throw new Error("Request body required");
}
if(reqBody) {
console.log("ReQBody--->"+reqBody);
fetchParams.headers["Content-type"] = "application/json";
fetchParams.body = reqBody;
}
const fetchPromise = await fetch(endPoint, fetchParams);
const timeOutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Request Timeout");
}, 3000);
});
const response = await Promise.race([fetchPromise, timeOutPromise]);
return response;
} catch (e) {
return e;
}
}
export const fetchApi = async (url, method, body, statusCode, token = null, loader = false) => {
console.log("In FetchAPi Function");
try {
const headers = {}
const result = {
token: null,
success: false,
responseBody: null
};
if(token) {
headers["securityKey"] = token;
}
const response = await api(url, method, body, headers);
console.log("fetchApi-->>"+response);
if(response.status === statusCode) {
result.success = true;
let responseBody;
const responseText = await response.text();
try {
responseBody = JSON.parse(responseText);
} catch (e) {
responseBody = responseText;
}
result.responseBody = responseBody;
return result;
}
let errorBody;
const errorText = await response.text();
try {
errorBody = JSON.parse(errorText);
} catch (e) {
errorBody = errorText;
}
result.responseBody = errorBody;
console.log("FetchApi(Result)--->>"+result);
throw result;
} catch (error) {
return error;
}
}
auth.actions.js
export const loginUser = (payload) => {
console.log("In LoginUser function2");
return async (dispatch) => {
<-----**I am not able to enter into this block**------>
try {
dispatch({
type: "LOGIN_USER_LOADING"
});
console.log("In LoginUser function3");
const response = await fetchApi("/login", "POST", payload, 200);
if(response.success) {
dispatch({
type: "LOGIN_USER_SUCCESS",
});
dispatch({
type: "AUTH_USER_SUCCESS",
token: response.token
});
dispatch({
type: "GET_USER_SUCCESS",
payload: response.responseBody
});
return response;
} else {
throw response;
}
} catch (error) {
dispatch({
type: "LOGIN_USER_FAIL",
payload: error.responseBody
});
return error;
}
}
}
In console log, I can't see anything in network tab. In the android emulator, the mentioned warning has come.
My console tab
I see that your BASE_URL is served using an http endpoint. You can only make requests to https endpoints from react native projects. A possible workaround is to use ngrok. Just download it and run ./ngrok http 8200 since your port number is 8200. It will expose an HTTPS endpoint and replace your BASE_URL with that link and try fetching the data again.
I use the following code to make API calls. See if you can integrate it in your code. it is quite simple:
In a class called FetchService:
class FetchService {
adminAuth(cb, data) {
console.log('here in the fetch service');
return fetch(
baseURL + "login",
{
method: "POST",
headers: {
Accept: "application/json",
},
body: data
}
)
.then((response) => response.json())
.then(responsej => {
cb(null, responsej);
})
.catch(error => {
cb(error, null);
});
}
}
export default FetchService;
Then call it from your component using:
import FetchService from './FetchService';
const fetcher = new FetchService;
export default class LoginScreen extends React.Component {
fetchData() {
const data = new FormData();
data.append('username',this.state.username);
data.append('password',this.state.password);
fetcher.wastereport((err, responsej) => {
if(err) {
//handle error here
} else {
//handle response here
}
}, data);
}
}

Promises with React [duplicate]

This question already has answers here:
What's the difference between returning value or Promise.resolve from then()
(6 answers)
Closed 4 years ago.
I have questions about promises. I'm just starting to deal with them and it's not that easy to understand!
I'm trying to setup an authentication system for my app.
RegisterPage
handleSubmit looks like that:
handleSubmit(event) {
event.preventDefault();
const { user } = this.state;
//some code here
userActions.register(user);
}
UserActions
function register(user) {
userService.register(user)
.then(
user => {
success(user);
},
error => {
failure(error.toString());
}
);
function success(user) { return { type: "REGISTER_SUCCESS", user } }
function failure(error) { return { type: "REGISTER_ERROR", error } }
}
UserService
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
Question 1. That code is "working" but not like I want. That way, even if the request success, I still can have error from the server, like duplicate username or something like that. I guess what I want is to return Promise.reject() not just if !response.ok but also if I have errors in the JSON returned right?
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
else if(data.errors) {
return Promise.reject(data.message);
}
return data;
});
}
Question 2. If everything's fine, should I return data or return Promise.resolve(data)? And why?
Checkout the documentation for fetch here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
It seems you should be using .catch() to get server errors and just use throw new Error() for having errors.
You don't really need to use Promise.resolve or Promise.reject.
To help refactor what you have, you can try this
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`/api/users/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text()
.then(text => {
if (!response.ok) {
const error = (data && data.message) || response.statusText;
throw new Error(error);
} else {
const data = text && JSON.parse(text);
return data;
}
})
.catch(error => throw new Error(err));
}

TypeError: undefined is not an object (evaluating '_this.props.auth(values.username, values.password).then')

I'm developing a ReactJS app.
I'm getting the following error "TypeError: undefined is not an object (evaluating '_this.props.auth(values.username, values.password).then')".
When the "return new Promise" is outside the "then" it works properly. Nonetheless, I want to return the promise after only the two first "then"s.
Sample of loginActions.js
export const auth = (username, password) => dispatch => {
fetch('http://localhost/webservices/login', {
method: 'post',
body: JSON.stringify({ username, password })
})
.then(res => {
if(res.ok) {
console.log("Succeeded.", res);
return res.json();
} else {
console.log("Failed.", res);
return res.json();
}
})
.then(json => {
if (json.token) {
auth_status.value = true;
return auth_status.value;
} else {
auth_status.value = false;
return auth_status.value;
}
})
.then(function(res){
return new Promise((resolve, reject) => {
dispatch({
type: VERIFY_AUTH,
payload: res
});
resolve();
})
})
.catch(err => {
console.error(err);
});
};
Sample of login.js
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log("Received values of form: ", values);
this.props.auth(values.username, values.password).then(() => {
if (this.props.auth_status === true) {
message.success("Welcome!", 3);
this.setState({
redirect: true
});
} else {
message.error("The username and password combination is incorrect", 3);
}
})
.catch(err => {
console.error(err);
});
}
});
};
Your action auth is not returning anything. The return statements in the asynchronous handlers do not return for the action itself.
You need to return a Promise in your auth() action that you resolve yourself in the third then:
export const auth = (username, password) => dispatch => {
// instantly return a new promise that
// can be resolved/rejected in one of the handlers
return new Promise((resolve, reject) => {
fetch('http://localhost/webservices/login', {
method: 'post',
body: JSON.stringify({
username,
password
})
}).then(res => {
if (res.ok) return res.json();
// your probably also want to reject here
// to handle the failing of the action
reject();
}).then(json => {
if (json.token) {
auth_status.value = true;
return auth_status.value;
} else {
auth_status.value = false;
return auth_status.value;
}
}).then(res => {
dispatch({
type: VERIFY_AUTH,
payload: res
});
// resolve the original promise here
resolve();
}).catch(err => console.error(err));
});
};

how to make multiple api request using axios

i am having exsiting service to make api call through axios in my react app,which i think is limited to one api request at a time,i wanted to make multiple request using axios.all,but i am not able to find way to modify the service,see below is the code
As in Action.js you can see that i combine two request which is wrong i guess so,please help me how to combine two request using axios.all,and please suggest api service implementation is correct or what can i do to improve it
APIService.js
import axios from 'axios';
import apiConfig from './apiConfig';
import UserSession from './userSession';
import history from '../utils/history/history';
const session = sessionStorage;
var axiosConfig = axios.create({
baseURL: apiConfig.baseUrl,
headers: {
Authorization: sessionStorage.getItem('token') != null ?
`Bearer ${sessionStorage.getItem('token')}` : null,
Accept: 'application/json',
'Content-Type': 'application/json'
},
timeout: 20000,
responseType: 'json'
});
axiosConfig.interceptors.request.use((config) => {
config.headers.Authorization =
sessionStorage.getItem('token') != null ? `Bearer
${sessionStorage.getItem('token')}` : null;
return config;
},(error) => Promise.reject(error));
const apiService = function(options) {
const onSuccess = function(response) {
if (response.status === 201) {
return Promise.resolve(
Object.assign(
{},
{
message: response.statusText
}
)
);
} else if (response.status === 200) {
if ((response.data && response.data !== null) || response.data !==
undefined || response.data !== '') {
return response.data;
} else {
return Promise.resolve(
Object.assign(
{},
{
message: response.statusText
}
)
);
}
} else if (response.data.length < 1) {
return Promise.reject(
Object.assign(
{},
{
message: 'No Data'
}
)
);
} else {
return response.data;
}
};
const onError = function(error) {
if (error.response) {
if (error.response.status === 401) {
sessionStorage.removeItem('token');
window.location = '/login';
return Promise.reject(error.response);
} else if (error.response.status === 404) {
return Promise.reject(
Object.assign(
{},
{
message: error.response.statusText
}
)
);
} else if (error.response.status === 500) {
return Promise.reject(
Object.assign(
{},
{
message: error.response.statusText
}
)
);
} else {
return Promise.reject(error.response.data);
}
} else if (error.request) {
// The request was made but no response was received
return Promise.reject(
Object.assign(
{},
{
message: error.message
}
)
);
//return Promise.reject(error.message);
} else {
// Something else happened while setting up the request
// triggered the error
return Promise.reject(
Object.assign(
{},
{
message: error.message
}
)
);
}
};
return axiosConfig(options).then(onSuccess).catch(onError);
};
export default apiService;
Request.js
import apiService from '../apiService';
export const FirstRequest = () => {
return apiService({
url: 'FirstURL',
method: 'get',
});
};
export const SecondRequest = () => {
return apiService({
url: 'SecondURL',
method: 'get',
});
};
Action.js
export const SomeHandler = () => (dispatch) => {
dispatch({
type: API_REQUEST
});
FirstRequest()
.then((res) => {
dispatch({
type: API_SUCCESS
});
SecondRequest().then((res) => {
dispatch({
type: API_SUCCESS
});
dispatch({ type: VIEW1, payload: res });
dispatch({ type: VIEW2, payload: res });
}).catch((err) => {
dispatch({
type: API_FAILURE,
payload: err
});
});
})
.catch((err) => {
dispatch({
type: API_FAILURE,
payload: err
});
});
};
This is not related to axios at all. You can combine two async functions together in an action method, using async library:
async.parallel([
getUsers,
getComments
],
function(err, results) {
// the results array will equal to [[], {'x': 'y'}] even though
// the second function had a shorter timeout.
// dispatch here
});
function getUsers(callback) {
callback(null, [])
}
function getComments(callback) {
callback(null, {'x': 'y'})
}
First off, not sure you want to do this in your componentWillMount, because your component will not render until all this is done, it's better to have it in componentDidMount and have some default states that will update once done with these requests. Second, you want to limit the number of setStates you write because they might cause additional re-renders, here is a solution using async/await:
async componentDidMount() {
const firstRequest = await axios.get(URL1);
const secondRequest = await axios.get(URL2);
const thirdRequest = await axios.get(URL3);
this.setState({
p1Location: firstRequest.data,
p2Location: SecondRequest.data,
p3Location: thirdRequest.data,
});
}
i'm working this way. you can use this
const token_config = {
headers: {
'Authorization': `Bearer ${process.env.JWD_TOKEN}`
}
}
const [ res1, res2 ] = await Axios.all([
Axios.get(`https://api-1`, token_config),
Axios.get(`https://api-2`, token_config)
]);
res.json({
info: {
"res_1": res1,
"res_2": res2
}
});

Categories

Resources