Showing alert from service class in react-native - react-native

I have an HTTP service class which is responsible for communication with my Laravel based API. I want the HTTP service class to trigger React Native Alert box when there is a 401 authentication issue. Or 403 Access denied issue or 422 which is validation issue.
I have the basic HTTP service class ready, but I am not able to show an alert from my HTTP service because it is not a react component and I want to know if I can use such a global service class and still trigger Native components.
Below is my code:
import axios from 'axios';
import {
AsyncStorage,
Alert
} from 'react-native';
class HttpService {
async get(url) {
let at = await AsyncStorage.getItem('access_token', (error, accessToken) => {
return accessToken;
});
let data = await axios.get(url, {headers: { 'Authorization': 'Bearer ' + at }})
.then(response => {
console.log('response', response);
return response;
})
.catch(error => {
let message, title = '';
if (!error.response) {
message = error;
}
if (error.response.status === 401) {
message = error.response.data.message;
title = 'Authentication issue'
}
Alert.alert(title, message, [
{ text: 'Ok', onPress: () => console.log(123) }
]);
});
return data;
}
post() {}
}
export default HttpService;

Why not just return the errors too? So you can determine what occurred so the component that makes the HttpService call knows when to show the Alert.
class yourComponent extends Component{
constructor() {
this.state = {
token: null,
isLoaded: false,
error: null
}
}
componentDidMount() {
HttpService.get('someUrl')
.then(res => {
this.setState({
token: res,
isLoaded: true
});
}, (error) => {
this.setState({
error: error,
isLoaded: true
});
})
}
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
// return your Alert here
} else if (!isLoaded) {
// return loading component;
} else {
// return your main component
}
}
}

Related

Nuxt fetch hook api can't access the api folder

I have an api that I call in my fetch() hook:
pages/pageOne.vue:
async fetch() {
const { store, error } = this.$nuxt.context;
try {
await store.dispatch("folderOne/apiOne", null, { root: true });
} catch (e) {
error({
message: "error"
});
}
}
then in my store I have apiOne action setup like this:
store/folderOne/index.js:
//importing the api file here
const actions = {
apiOne({ commit }) {
apiFile.apiOne().then(data => {
if(data && data.status === 200){
// then commit
}
})
.catch(error => {
console.log(error);
});
},
}
Then I have a file for my APIs setup like this:
api/apiFile.js:
import axios from "axios";
const httpClient = axios.create({
headers: {
key: 'value
}
});
const baseUrl = process.env.config.baseUrl;
export default {
apiOne() {
return httpClient.get(`${baseUrl}values);
},
}
It doesn't work. But when I call the same apiOne in a #click method it works. Anyone knows what's is wrong and how can I fix it?
your store is and should be a global construct of your application.
it means when you call for a action and your setup is correct, you don't have to handle directives/folders by yourself.
in your case all you should do is dispatch the action in your component like this:
async fetch() {
const { store, error } = this.$nuxt.context;
try {
await store.dispatch("apiOne", null, { root: true });
} catch (e) {
error({
message: "error"
});
}
}

How to change this class into a functional component in react native

Hello I'm using this axios wrapper in my project ,it's working as expected.But I want to use hooks in this class so I must change this into a functional component.Any ideas to change this into a functional component.Thanks...
import React from 'react';
import axios from 'axios';
import createAuthRefreshInterceptor from 'axios-auth-refresh';
const axiosApiInstance = axios.create({baseURL: "http://10.0.2.2:5001"});
const refreshAuthLogic = (failedRequest) => {
const options = {
method: 'post',
url: 'http://10.0.2.2:5001/api/token/refresh',
data: {
email: 'rwar#gmail.com',
refreshToken: 'testrefreshtoken'
},
};
return axios(options, {
pauseInstanceWhileRefreshing: true,
}).then((tokenRefreshResponse) => {
failedRequest.response.config.headers['Authorization'] =
'Bearer ' + tokenRefreshResponse.data.result.token;
return Promise.resolve();
});
};
createAuthRefreshInterceptor(axiosApiInstance, refreshAuthLogic);
axiosApiInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error && error.response && error.response.status === 401) {
// 401 error redirect to login
return Promise.reject(error);
}
if (error.response.status !== 401) {
return new Promise((resolve, reject) => {
reject(error);
});
}
},
);
export default axiosApiInstance;

Issue while trying to post / get an API on expo

I'm new to react-native and it's my first app.
I'm trying to develop my app and connect it to my API. I develop all my app with the navigator view on Expo and there is no problem, the connection is good and I can get or post everything.
Now that I'm trying to fetch it with expo on my Android or Apple, there is no response.
Here is my code for the authentication:
login.js
import { post } from '../request/post';
export const login = (mail, pass) => {
console.log(mail)
console.log(pass)
console.log("POST request for login");
return post('/api/login', {
email: mail,
password: pass,
mobile: true
});
};
post.js
import { API_URL } from '../url';
import { getStorageToken } from '../../utils/asyncStorage';
const getHeaders = async () => {
const token = await getStorageToken();
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json'
};
if (token !== 'undefined' && token.length > 0) {
headers['auth'] = `${token}`;
}
return headers;
};
export const post = async (destination, body) => {
const headers = await getHeaders();
const result = await fetch(`${API_URL}${destination}`, {
method: 'POST',
headers,
body: JSON.stringify(body),
});
console.log(result);
if (result.ok) {
return await result.json();
}
throw { error: result.status };
};
loginPage.js
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Image, TextInput, Linking } from 'react-native';
import { setStorageAfterConnection } from '../../utils/asyncStorage';
import { CheckBox, Icon } from 'react-native-elements';
import { login } from '../../api/auth/login';
export default class LogIn extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errorMessage: ''
};
}
submit = () => {
login(this.state.email, this.state.password)
.then(async (res) => {
await setStorageAfterConnection(res);
this.props.navigation.navigate('Home');
})
.catch((res) => {
if (res && res.error) {
this.setState({ errorMessage: res.error});
}
this.setState({ errorMessage: "Erreur de connexion"});
});
};
render() {
return (
...............................................
);
}
}
I tried to debug it and it seems to not find the function post() because I don't have any network request. I do not know what's the correct way to do an "API" component so I think I probably made some mistakes but I didn't find what I'm missing.
I used Lan connection and my API isn't hosted on local.
Regards,
Try to add async-await to "login":
export const login = async (mail, pass) => { <---- 'async' ADDED
console.log(mail)
console.log(pass)
console.log("POST request for login");
return await post('/api/login', { <---- 'await' ADDED
email: mail,
password: pass,
mobile: true
});
};
I tried to put some debug on my code:
export const post = async (destination, body) => {
console.log("A");
const headers = await getHeaders();
console.log("B")
const result = await fetch(`${API_URL}${destination}`, {
method: 'POST',
headers,
body: JSON.stringify(body),
});
console.log(result);
if (result.ok) {
return await result.json();
}
throw { error: result.status };
};
And I get on the console:
email
password
POST request for login
A
So the problem seems to be on my await getHeaders()
EDIT: Problem was solved. It was because of the getHeaders that try to get the token and failed.

How can I properly test my React Native OAuth wrapper component?

I have written a React Native "Auth Portal" component, that links with an existing OAuth portal and handles getting the auth-code from the redirect URI and the subsequent token exchange request. It seems to be working well, but clearly I need to test this assumption, so I am trying to write unit/functional tests. How can I properly do this?
I originally considered extracting the functions used in the two useEffects out into separate, isolated functions and taking, for example, the authCode as an argument instead of from state and mocking this input.
However, I believe a better strategy is to test the component as a whole and just mock the response to the axios post request, comparing that mock to what get's stored in the AsyncStorage, as well as mocking a bad request/response to test the error handling.
Is this a good approach?
import axios from 'axios'
import AsyncStorage from '#react-native-community/async-storage'
import React, { useEffect, useState } from 'react'
import { Linking } from 'react-native'
import InAppBrowser from 'react-native-inappbrowser-reborn'
import { LoadingIndicator } from '../LoadingIndicator'
interface AuthPortalProps {
client_id: string
scopes: string[]
client_secret: string
redirect_uri: string
onAuthComplete: () => void
onError: () => void
}
interface ApiDataResponse {
token_type: string
expires_in: number
access_token: string
refresh_token: string
}
export const AuthPortal = ({
client_id,
scopes,
client_secret,
redirect_uri,
onAuthComplete,
onError,
}: AuthPortalProps) => {
const [authCode, setAuthCode] = useState()
const getAuthCodeFromRedirectUri = async (url: string) => {
if (url.includes('code=')) {
const regex = /[^=]+$/g
const code = url.match(regex)!.toString()
await setAuthCode(code)
}
}
useEffect(() => {
const getAuthCode = async () => {
const url = `https://example.com/auth/?response_type=code&client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scopes}`
if (!authCode) {
try {
InAppBrowser.openAuth(url, redirect_uri).then(response => {
if (response.type === 'success' && response.url && response.url.includes('code=')) {
getAuthCodeFromRedirectUri(response.url)
Linking.openURL(redirect_uri)
}
})
} catch (error) {
console.log('Error: ', error.message)
onError()
}
}
}
getAuthCode()
return () => {
InAppBrowser.closeAuth()
}
}, [authCode, client_id, onError, redirect_uri, scopes])
useEffect(() => {
const getAuthRefreshToken = async () => {
if (authCode) {
try {
const { data }: { data: ApiDataResponse } = await axios.post(
'https://example.com/auth',
{
grant_type: 'authorization_code',
client_id: `${client_id}`,
code: `${authCode}`,
client_secret: `${client_secret}`,
redirect_uri: `${redirect_uri}`,
}
)
await Promise.all([
AsyncStorage.setItem('access_token', data.access_token),
AsyncStorage.setItem('refresh_token', data.refresh_token),
])
setTimeout(() => {
onAuthComplete()
}, 1000)
} catch (error) {
if (error.response) {
console.log('Error: ', error.response)
} else if (error.request) {
console.log('Error: ', error.request)
} else {
console.log('Error: ', error.message)
}
onError()
}
}
}
getAuthRefreshToken()
}, [authCode, client_id, client_secret, onAuthComplete, onError, redirect_uri])
return <LoadingIndicator />
}

React — Requesting data using Fetch

I am trying to get some data from an API using Fetch without success. For some reason the request is failing and I am not able to render the data... as I am quite new to React and Fetch I am not sure where the error is. Is it something to do with the way I am requesting the API?
Thank you in advance
class App extends React.Component {
render() {
return <Data />
}
}
class Data extends React.Component {
constructor(props) {
super(props)
this.state = {
requestFailed: false,
}
}
componentDidMount() { // Executes after mouting
fetch('https://randomuser.me/api/')
.then(response => {
if (!request.ok) {
throw Error("Network request failed.")
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
data: d
})
}, () => {
this.setState({
requestFailed: true
})
})
}
render() {
if(this.state.requestFailed) return <p>Request failed.</p>
if(!this.state.data) return <p>Loading</p>
return (
<h1>{this.state.data.results[0].gender}</h1>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
CodePen
As mentioned on the GITHUB docs, you can implement the fetch like
fetch('https://randomuser.me/api/')
.then((response) => {
return response.json()
}).then((d) => {
console.log('parsed json', d)
this.setState({
data: d
});
}).catch(function(ex) {
console.log('parsing failed', ex)
this.setState({
requestFailed: true
})
})
CODEPEN
fetch method should be
fetch('your_url')
.then (
response => {
if (response.status !== 200) {
return 'Error. Status Code: ' + response.status
}
response.json().then(result => console.log(result)) // do sth with data
}
)
.catch(function(err) {
console.log('Opps Error', err)
})
I think your problem is with
.then(response => {
if (!request.ok) {
throw Error("Network request failed.")
}
return response
})
There's no request object that has the property ok. Maybe you mean to check response.ok ?
.then(response => {
if (!response.ok) {
throw Error("Network request failed.")
}
return response
})