I run a React Native app on Android emulator but found networking does not work, I run it on iOS it works fine.
Here is the simple code:
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
export default () => {
React.useEffect(() => {
const useFetch = async () => {
try {
console.log('fetch ...');
let response = await fetch('https://mytestdomain.com');
console.log(response.status);
// let json = await response.json();
} catch (error) {
console.log(error);
}
};
useFetch();
const useXMLHttpRequest = async () => {
try {
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
console.log(request.readyState);
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
} else {
console.warn('error');
}
};
request.open('GET', 'https://mytestdomain.com');
request.send();
} catch (error) {
console.log(error);
}
};
useXMLHttpRequest();
}, []);
return (
<View style={styles.layout}>
<Text>React Native Android networking</Text>
</View>
);
};
const styles = StyleSheet.create({
layout: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
By call useFetch() I just see 'fetch ...' in console, even can not see response.status, and there is no error from catch.
By call useXMLHttpRequest() I see request.readyState is 1.
Thanks advance for any help
You probably should give this asynchronous log a more explicit message to find this specific one among other logs, like :
console.log("RS : " + response.status);
I tried to reproduce your issue, and I can't, it works on my Android simulator just fine.
Would you please share your console logs ?
A workaround is to use the #react-native-community/netinfo library to determine whether you have internet access or not.
Beware, it could be slow.
This library provides a listener with the connection details changes. Getting the connection details can be slow... you would have to test it on real devices to determine whether this workaround is efficient or not.
Quick Demo based on your source code :
[...]
import { StyleSheet, View, Text } from "react-native";
+import NetInfo from "#react-native-community/netinfo";
export default () => {
- React.useEffect(() => {
+ NetInfo.addEventListener((netInfo) => {
const useFetch = async () => {
try {
- console.log("fetch ...");
- let response = await fetch("https://mytestdomain.com");
- console.log(response.status);
- // let json = await response.json();
+ // Just because there is a connection, it does not mean that internet is accessible, so test both isConnected and isInternetReachable
+ if (netInfo.isConnected && netInfo.isInternetReachable) {
+ console.log("fetch ...");
+ let response = await fetch(
+ "https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"
+ );
+ console.log("start 1");
+ console.log("success 1", response.status);
+ console.log("stop 1");
+ } else {
+ console.log("internet is not ready");
+ }
} catch (error) {
[...]
Related
I'm new on Ble-Plx and React-Native, I'm working on a project where we should be able to connect to a device and change the behavior, just writing new characteristics and it should change... I used a Ble Starter that I found on the internet, and I was able to write the characteristics and change the behavior. So I know the peripheral can do it and that I only need to develop the code. I have something already but when I write the characteristics the device doesn't change as we spect, the device does NOTHING and I don't get any error or something Just the device does nothing. Here is a piece of code if someone can help would be amazing!
HERE IS THE MAIN PART OF THE CODE:
import { BleManager } from 'react-native-ble-plx';
const manager = new BleManager();
const Example=()=>{
useEffect(() => {
manager.onStateChange((state) => {
const subscription = manager.onStateChange((state) => {
if (state === 'PoweredOn') {
scanAndConnect();
subscription.remove();
}
}, true);
return () => subscription.remove();
});
}, [manager])
function scanAndConnect() {
console.log('Escanear')
manager.startDeviceScan(null,null,async(error,device)=>{
console.log(device.id)
if(device.id ==='D1:42:78:C8:AB:FB' || device.id ==='D1:42:BF:F1:D9:3C'){
manager.stopDeviceScan()
console.log("ID del dispositivo: ", device.id)
console.log("Nombre del dispositivo: ", device.name)
console.log("RRSI del dispositivo: ", device.rssi)
console.log("MTU del dispositivo: ", device.mtu)
device.connect()
.then((device) => {
const services = device.discoverAllServicesAndCharacteristics()
console.log(services)
})
.catch((error) => {
// Handle errors
console.log(error)
});
}
if (error) {
console.log(error)
return
}
})
}
const writeChar=async()=> {
var text = (Buffer.from("AA0100000200").toString('base64'));
manager.writeCharacteristicWithoutResponseForDevice(
'D1:42:BF:F1:D9:3C',
'aae0',
'aae1',
text,
)
.then(() => {
console.log("Write: " + text);
})
.catch((error) => {
console.log(error);
});
}
AND HERE IS WHAT THE CONSOLE.LOG GIVE ME BACK.
D1:42:BF:F1:D9:3C
LOG ID del dispositivo: D1:42:BF:F1:D9:3C
LOG Nombre del dispositivo: Bluetrum-MX
LOG RRSI del dispositivo: -73
LOG MTU del dispositivo: 23
LOG {"_U": 0, "_V": 0, "_W": null, "_X": null}
LOG Write: QUEwMTAwMDAwMjAw
BUT even that there is no error the device doesn't change so there is my problem, I'm NOT able to make the device change the behavior and start the vibration (just by writing the characteristics)
Put await before manager while writing characteristics
and try below code
const writeChar=async()=> {
var text = (Buffer.from("AA0100000200").toString('base64'));
await manager.writeCharacteristicWithoutResponseForDevice(
'D1:42:BF:F1:D9:3C',
'aae0',
'aae1',
text,
)
.then((res) => {
console.log("Write: " + text);
console.log("res",res)
})
.catch((error) => {
console.log(error);
});
}
I'm using the following code to download a file (can be a PDF or a DOC) and then opening it using Linking.
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
APIHelpers.getDefaultHeaders()
)
.then((res) => {
let status = res.info().status;
if (status == 200) {
Linking.canOpenURL(res.path())
.then((supported) => {
if (!supported) {
alert('Can\'t handle url: ' + res.path());
} else {
Linking.openURL(res.path())
.catch((err) => alert('An error occurred while opening the file. ' + err));
}
})
.catch((err) => alert('The file cannot be opened. ' + err));
} else {
alert('File was not found.')
}
})
.catch((errorMessage, statusCode) => {
alert('There was some error while downloading the file. ' + errorMessage);
});
However, I'm getting the following error:
An error occurred while opening the file. Error: Unable to open URL:
file:///Users/abhishekpokhriyal/Library/Developer/CoreSimulator/Devices/3E2A9C16-0222-40A6-8C1C-EC174B6EE9E8/data/Containers/Data/Application/A37B9D69-583D-4DC8-94B2-0F4AF8272310/Documents/RNFetchBlob_tmp/RNFetchBlobTmp_o259xexg7axbwq3fh6f4.pdf
I need to implement the solution for both iOS and Android.
I think the easiest way to do so is by using react-native-file-viewer package.
It allows you to Prompt the user to choose an app to open the file with (if there are multiple installed apps that support the mimetype).
import FileViewer from 'react-native-file-viewer';
const path = // absolute-path-to-my-local-file.
FileViewer.open(path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
So, I finally did this by replacing Linking by the package react-native-file-viewer.
In my APIHelpers.js:
async getRemoteFile(filePath, extension, method = 'GET') {
const remoteUrl = `${API_BASE_URL}/${encodeURIComponent(filePath)}`;
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
return new Promise(async (next, error) => {
try {
let response = await RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
this.getDefaultHeaders()
);
next(response);
} catch (err) {
error(err);
}
});
}
In my Actions.js
export function openDocument(docPath, ext) {
return async (dispatch) => {
dispatch(fetchingFile());
APIHelpers.getRemoteFile(docPath, ext).then(async function(response) {
dispatch(successFetchingFile());
let status = response.info().status;
if (status == 200) {
const path = response.path();
setTimeout(() => {
FileViewer.open(path, {
showOpenWithDialog: true,
showAppsSuggestions: true,
})
.catch(error => {
dispatch(errorOpeningFile(error));
});
}, 100);
} else {
dispatch(invalidFile());
}
}).catch(function(err) {
dispatch(errorFetchingFile(err));
});
}
}
In my Screen.js
import { openDocument } from 'path/to/Actions';
render() {
return <Button
title={'View file'}
onPress={() => this.props.dispatchOpenDocument(doc.filepath, doc.extension)}
/>;
}
.
.
.
const mapDispatchToProps = {
dispatchOpenDocument: (docPath, ext) => openDocument(docPath, ext),
}
Are you downloading it from the web? I can see the pdf path is attached at the end of the error path.
For web URLs, the protocol ("http://", "https://") must be set accordingly!
Try to append appropriate schemes to your path. Check it out from the link mentioned below.
This can be done with 'rn-fetch-blob'
RNFetchBlob.android.actionViewIntent(fileLocation, mimeType);
What my app should do is it uses google oauth for login and the server will then return a create a user (if user has not been created) and log the user in (by creating session).
I am certain my backend works because i have tested everything with postman.
However, i am unable to handle the creation of user (if user has not been created) and console.log my jwt to assure myself that it is working. I am strongly convinced it has to do with my react native code below.
Furthermore, the log which says "this is not working" is not being printed in google chrome as well.
-
import React from 'react';
import { StyleSheet, Platform, Image, Text, View, ScrollView } from 'react-native';
import { GoogleSignin, statusCodes, GoogleSigninButton } from 'react-native-google-signin';
import NavigationManager from './containers/TabNavigator';
import PhoneStorageManager from './assets/jwtStorageManager';
GoogleSignin.configure({
forceConsentPrompt: true,
webClientId: '<client-id>
});
export default class App extends React.Component {
constructor() {
super();
this.state = {failed: "NOOO", user: null, jwt: null};
}
async componentDidMount() {
}
signIn = async () => {
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
this.setState({ failed: "SUCCESS" });
this.setState({ user: userInfo });
const resp = await fetch('<url>', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
session: {
token: userInfo.idToken,
authprovider: "google"
}
})
})
.then(response => response.json())
this.setState({ jwt: resp.jwt }, ()=> {
console.log(this.state.jwt, 'jwt');
PhoneStorageManager._storeData("key",this.state.jwt);
//so that i can use jwt for further api calls
});
console.log("this is not working");
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
this.setState({ failed: "CANCELLED" });
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (f.e. sign in) is in progress already
this.setState({ failed: "PROGRESS" });
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
this.setState({ failed: "NO AVAIL" });
} else {
// some other error happened
this.setState({error: error});
this.setState({ failed: "some other error happened" });
}
}
}
render() {
if (this.state.jwt) {
console.log(this.state.jwt);
return (
<NavigationManager JWT={this.state.jwt}/>
);
}
else
return (
<View style={styles.container}>
<Image source={require('./assets/auditlogo.png')} style={[styles.logo]} />
<GoogleSigninButton
style={{ width: 192, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={this.signIn}
disabled={this.state.isSigninInProgress} />
<Text style= {styles.instructions}>
Please Sign in to continue.
</Text>
</View>
);
}
}
1) User presses google sign in button
2) Fetch api to create user if new user.
3) Fetch api to create session. (this returns jwt for further api calls)
4) Further api calls which uses jwt. (How do i get the jwt to be passed down?)
I want to send sms to multiple numbers without opening to default messaging app.
I try to use react-native-sms-x but its not maintained and my project just stuck at compiling.
Also I used react-native-sms but it open default Messaging App filled with one user number and message body and had to click send button of it too.
import { Linking,Platform } from "react-native";
const url = (Platform.OS === 'android')
? 'sms:919999999999?body=your message'
: 'sms:919999999999'
Linking.canOpenURL(url).then(supported => {
if (!supported) {
console.log('Unsupported url: ' + url)
} else {
return Linking.openURL(url)
}
}).catch(err => console.error('An error occurred', err))
After a lot of research and trials in the react app...
I have found this library working fine and reached the goals to send a message without going into the default message environment.
var phoneNumbers = {
"addressList": ["+911212121212", "+911212121212"]
};
var message = "This is automated test message"
SmsAndroid.autoSend(
phoneNumbers,
message,
(fail) => {
console.log('Failed with this error: ' + fail);
},
(success) => {
console.log('SMS sent successfully');
},
);
I hope it helps you. Do not forget to upvote
From Now Just For Android I use react-native-sms-android
Here is my Code for Sending sms to multiple users:
import Asms from "react-native-sms-android";
type Props = {};
export default class App extends Component<Props> {
constructor(Props) {
super(Props);
this.state = { FileNumbers: ['687867867867','8575774433'], Message:
"gjjgjgj" };
}
sendingSms = (Receivers, Messagex) => {
try {
Receivers.map(
async Numbers =>
await Asms.sms(Numbers, Messagex, "sendDirect", (err,message)
=> {
if (err) {
console.log(err);
} else {
console.log(message);
}
})
);
} catch (e) {
alert("" + e);
}
};
render() {
return (
<View style={styles.container}>
<TextInput
style={{
height: 40,
borderColor: "gray",
borderWidth: 1,
width: "90%"
}}
onChangeText={Message => this.setState({ Message })}
value={this.state.Message}
/>
<Button
title="SEND"
onPress={() =>
this.sendingSms(this.state.FileNumbers, this.state.Message)
}
/>
</View>
);
}
}
Fairly new to React native and its concepts. I have been playing with RN for a while to create an application to fetch API data from
http://jsonplaceholder.typicode.com/photos
I have been looking into the documentation of AsyncStorage to implement how i can cache the API data so that upon terminating the application, it doesn't have to deal with fetching the data from web again and again, but wasn't successfully able to implement it.
It will be great if you can provide me help/suggestion based on it. I have included my source code for the 2 important files in my application, along with the a Test.js file with how i was trying to achieve.
import React, {Component} from 'react';
import { FlatList, View, Text, AsyncStorage, ActivityIndicator } from 'react-native';
import axios from 'axios';
import GalleryDetail from './GalleryDetail';
class GalleryList extends Component {
state = { photos: []};
componentDidMount() {
axios.get('http://jsonplaceholder.typicode.com/photos')
.then(response => this.setState({ photos: response.data }))
.catch((error)=> console.warn("fetch Error: ", error));
}
getPhotos = async()=> {
try {
photos = await AsyncStorage.getItem('GalleryPhotos');
}
catch (error) {
console.error(error);
}
}
savePhotos(){
AsyncStorage.setItem('GalleryPhotos', this.state.photos);
console.log('works !');
}
renderPhoto = ({item})=> {
return <GalleryDetail photo={item}/>
}
keyExtractor = (photo, index) => photo.id;
render () {
if(!this.state.photos){
return <ActivityIndicator/>;
}
return (
<FlatList
data = {this.state.photos}
keyExtractor={this.keyExtractor}
renderItem={this.renderPhoto}
/>
);
}
}
export default GalleryList;
and GalleryDetail linked with GalleryList-
import React, {Component} from 'react';
import { Text, View, Image } from 'react-native';
import Card from './Card';
import CardSection from './CardSection';
const GalleryDetail = (props)=> {
return (
<Card>
<CardSection style = {styles.headerContentStyle}>
<Image
style={styles.thumbnailStyle}
source = {{ uri: props.photo.thumbnailUrl}}/>
<Text style= {styles.textStyle}>{props.photo.title} </Text>
</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
thumbnailStyle: {
height: 60,
width: 60
},
textStyle: {
fontSize: 12,
//textAlign: 'right',
flexDirection: 'row',
justifyContent: 'flex-end',
flex: 1,
flexWrap: 'wrap',
marginLeft: 5,
marginRight: 5,
}
}
export default GalleryDetail;
My method of trying was that-
Upon launching the application, it will first look in asyncStorage, if it finds the data- it fetches from async otherwise going to the web,fetching and storing again for later use.
I tried to implement somewhat like this in a separate file since i dint wanted to breakdown my already running app. The weird broken syntax is
State = {
photos: []
}
componentDidMount() {
// just a variable acting to fetch data from the stored keyvalue pair
check = AsyncStorage.getItem("PhotosKey").then((response) => {
this.setState({"PhotosKey": response});
}).done();
if(check) {
console.log('Data was fetched!!!!!');
check();
}
else {
console.log("Data was not fetched!");
var Data = axios.get('http://jsonplaceholder.typicode.com/photos').
then(response => this.setState({ photos: response.data })).
catch((error)=> console.warn("fetch Error: ", error));
}
}
Thanks in advance!
async componentDidMount() {
const photoStorage = await AsyncStorage.getItem('GalleryPhotos')
if(photoStorage) {
try {
const photoResp = await axios.get('http://jsonplaceholder.typicode.com/photos')
const photoData = await JSON.stringify(photoResp.data)
await AsyncStorage.setItem('GalleryPhotos', photoData);
} catch(e) {
console.warn("fetch Error: ", error)
}
.then(response => this.setState({ photos: response.data }))
}
}
later
getPhotos = async()=> {
try {
photos = JSON.parse(await AsyncStorage.getItem('GalleryPhotos'));
}
catch (error) {
console.error(error);
}
}
The approach from Subramanya is basically all you need to get started, I'm just going to introduce a state management approach with redux-persist where you can definitely appreciate when your app grows.
Redux Persist is performant, easy to implement, and easy to extend.
Let say you have your app hooked up with redux and implemented a fairly organised state tree, redux-persist stores the entire app state with AsyncStorage or any storage engine of your choice.
For instance, let's assume that your API endpoint returns a collection of photos, all you need to do is update the store, and your users can expect their data is safe and saved with redux-persist.
I have not tested all the code below
Let's define the store first,
import { AsyncStorage } from 'react-native';
import { createStore, compose, applyMiddleware, } from "redux";
import { persistStore } from "redux-persist";
import ReduxThunk from "redux-thunk";
import reducers from "../reducers"
const middleWare = [ReduxThunk]
const store = createStore(
reducers,
{},
compose(applyMiddleware(...middleWare))
)
// you can define more parameters, like blacklist or whitelist a reducer
// also, specify storage engine
persistStore(store, { storage: AsyncStorage });
export default store;
At your app's entry point,
import React, { Component } from "react";
import { Provider } from "react-redux";
import Router from "./Router";
import store from './store';
export default class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Provider store={store}>
<Router /> // navigator
</Provider>
);
}
}
Finally, your API logic.
// action creator
export storePhoto = photos => {
return {
type: 'STORE_PHOTOS',
payload: photos
}
}
// photos reducer
import { REHYDRATE } from 'redux-persist/constants';
export default (state = {}, action) => {
switch (action.type) {
case STORE_PHOTOS:
return { ...state, photos: action.payload }
// this is where `redux-persist` handles caching
case REHYDRATE:
var incoming = action.payload;
if(incoming) return { ...state, ...incoming }
return state;
default:
return state;
}
};
To retrieve data, you will see that redux abstracts away all the excess logics and there is no more setItem, getItem because redux-persist does that automagically for your already.
import { connect } from "react-redux";
import { storePhotos } from "./actions";
class GalleryList extends Component {
async componentDidMount() {
const photos = await axios.get('http://jsonplaceholder.typicode.com/photos');
storePhoto(photos)
}
renderPhoto = ({ item }) => <GalleryDetail photo={item}/>
keyExtractor = (photo, index) => photo.id;
render () {
return (
<FlatList
data = {this.props.photos}
keyExtractor={this.keyExtractor}
renderItem={this.renderPhoto}
/>
);
}
}
// pull data from photos reducer
const mapStateToProps = ({ photos }) => {
return {
photos: photos.photos
}
}
export default connect(mapStateToProps, { storePhotos })(GalleryList);
To summarise,
Install redux-persist in you project.
Import persistStore and autoRehydrate form redux-persist.
Add autoRehydrate to your store.
Pass your store to persistStore.
Listen to the persist/REHYDRATE action on your reducer and populate state accordingly.
Hope my answer helps!
Answer
Caching the data for a specific period of time
const cacheIntervaInHours = 24
const cacheExpiryTime = new Date()
cacheExpiryTime.setHours(cacheExpiryTime.getHours() + cacheIntervalInHours)
const lastRequest = await AsyncStorage.getItem("lastRequest")
if (lastRequest == null || lastRequest > cacheExpiryTime) {
fetch(`${apiUrl}/blogPosts/recent`)
.then(async (response) => {
return await response.json()
})
.then(async (json) => {
if (!json || json.length == 0) {
throw new Error()
}
AsyncStorage.setItem("lastRequest", new Date());
return await AsyncStorage.setItem('blogPosts', JSON.stringify(json))
})
.catch(error => {
console.error(error)
})
}