rtk query show when internet is off? - react-native

I use rtk query.
if I close my internet connection and navigate to a screen I see no Loader and or something else I see nothing. So when I turn on my internet connection then nothing happens. How can I say to the user when the internet is offline that I send him a message and when Internet is on that the rtk query automatically refetch the data ?
Cart.tsx
const ShoppingCart: React.FC<Props> = ({}) => {
const user_id = useSelector((state: RootState) => state.Auth.user.user_id);
const { data, isFetching, isLoading } = useCartQuery({user_id});
if(isLoading) {
return (
<Loader />
)
} else if(data && data.length > 0) {
return (
<ShoppingCartComponent
products={data}
/>
)
} else if(data && data.length === 0) {
return (
<ShoppingCartEmpty />
)
}
}
export default ShoppingCart;
Cart.ts API
reducerPath: 'ShoppingCartApi',
baseQuery: fetchBaseQuery({ baseUrl: `${API_ENDPOINT}/` }),
tagTypes: ['CART'],
endpoints: (builder) => ({
cart: builder.query<IProduct[], { user_id?: number; unlogged_uuid?: string; }>({
query: (data) => ({
url: '/cart',
method: 'POST',
body: data
}),
providesTags: ['CART']
}),

You can use the navigator.onLine to check if the user's device is currently online. Then listen for the online event, which is fired when the device's internet connection is restored and refetch data.
const ShoppingCart: React.FC<Props> = ({}) => {
const user_id = useSelector((state: RootState) => state.Auth.user.user_id);
const { data, isFetching, isLoading, refetch } = useCartQuery({user_id});
useEffect(() => {
const handleOnlineEvent = () => {
// Refetch data from the API when the online event is fired
refetch();
};
window.addEventListener('online', handleOnlineEvent);
return () => {
window.removeEventListener('online', handleOnlineEvent);
};
}, []);
if (!navigator.onLine) {
return (
<div>
<p>You are currently offline. Please check your internet connection and try again.</p>
</div>
);
}
if (isLoading) {
return (
<Loader />
)
} else if(data && data.length > 0) {
return (
<ShoppingCartComponent
products={data}
/>
)
} else if(data && data.length === 0) {
return (
<ShoppingCartEmpty />
)
}
}

Related

Multiple useEffect in react-native to achieve mentioned functionality

I need help with the async nature of Async storage and axios api. Here's the functionality that I am trying to achieve ->
send request to two separate api to get some data.
display that data on the screen with some additional text
api request are authenticated so a token is passed as Authentication Header
I have attached the current implementation, I am having the a number of errors in this
Errors:
Login_token not set in state after fetching from Async Storage.
Data not set in state after api call
both resulting in either failed api calls or undefined state errors on render
This is my code.
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react';
import { StyleSheet, View} from 'react-native';
// chat
import { GiftedChat } from 'react-native-gifted-chat';
// navigation
import { RootStackParamList } from '../../navigators/RootStack';
import { StackScreenProps } from '#react-navigation/stack';
export type Props = StackScreenProps<RootStackParamList, "Chat">;
// api
import { Convo_details, Send_Msg, List_Msg, Expert_Public_Profile } from '../../api/UserApi';
import Spinner from 'react-native-loading-spinner-overlay';
import AsyncStorage from '#react-native-async-storage/async-storage';
import uuid from 'react-native-uuid';
const Chat: FunctionComponent<Props> = ({ navigation, route, ...props }) => {
// console.log(props.route.params);
const [login_token, setlogin_token] = useState('')
const [conversation_id, setconversation_id] = useState('')
const [conversation_details, setconversation_details] = useState({})
const [currentuser, setcurrentuser] = useState({})
const [loading, setLoading] = useState(false);
const [expertuid, setexpertuid] = useState('')
const [ExpertProfile, setExpertProfile] = useState({})
const [messages, setMessages] = useState([]);
useEffect(() => {
getlogintoken()
console.log("####################################","getlogintoken");
}, [])
/* conversationid */
useEffect(() => {
if (route.params != null) {
setconversation_id(route.params[0])
}
console.log("####################################","conversation id");
}, [])
/* expert uid */
useEffect(() => {
if (route.params != null) {
setexpertuid(route.params[1])
}
console.log("####################################","expert uid");
}, [])
/* expert public profile */
useEffect(() => {
getexpertpublicprofile()
getConvo_details()
console.log("####################################","convo_details");
}, [])
useEffect(() => {
// get current user
AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
console.log("####################################","current user");
}, [])
// set welcome msg
useEffect(() => {
if (Object.keys(conversation_details).length != 0 && Object.keys(ExpertProfile).length != 0)
setwelcomemsg()
}, [])
const onSend = useCallback(async (messages = []) => {
// console.log(messages[0].text);
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
const data = {
conversation_id: "f98d6851-a713-4f58-9118-77a779ff175f",//conversation_id,
message_type: "TEXT",
body: messages[0].text
}
const res: any = await Send_Msg(data, login_token)
.catch(error => {
alert(`Send_Msg -> ${error}`)
console.log(error);
return
})
if (res.status == 200) {
console.log(res.data);
} else console.log(res);
}, [])
const getexpertpublicprofile = async () => {
setLoading(true)
const res: any = await Expert_Public_Profile(expertuid, login_token)
.catch(error => {
setLoading(false)
console.log("Expert public profile ->");
alert(`Expert public profile ->${error.message}`)
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setExpertProfile(res.data)
else {
alert(`get expert public profile${res.data.message}`)
console.log("getexpertpublicprofile -->");
console.log(res.data);
}
}
const getlogintoken = () => {
AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getConvo_details = async () => {
setLoading(true)
const res: any = await Convo_details(conversation_id, login_token)
.catch(error => {
setLoading(false)
alert(`Convo_details-->${error.message}`)
console.log("Convo_details -->");
console.log(error);
return
})
setLoading(false)
if (res.status === 200) setconversation_details(res.data)
else {
alert(`get convo details-> ${res.data.message}`)
console.log("getConvo_details -->");
console.log(res.data);
}
}
const setwelcomemsg = () => {
try {
let user = JSON.parse(currentuser)
let messages = [
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `About Me - ${ExpertProfile.bio}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `My name is ${conversation_details.recipient.name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
},
{
_id: uuid.v4().toString(),
conversation_id: conversation_details.conversation_id,
created_at: new Date(),
from: conversation_details.recipient.user_uid,
type: "TEXT",
text: `Hi ${user.full_name}`,
user: {
_id: conversation_details.recipient.user_uid,
}
}]
setMessages(previousMessages => GiftedChat.append(previousMessages, messages))
} catch (error) {
console.log("try -> set welcome msg");
console.log(error);
return
}
}
return (
<View style={styles.maincontainer}>
<Spinner
visible={loading}
textContent={'Loading...'}
textStyle={{ color: '#FFF' }}
/>
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user={{
_id: currentuser.user_uid,
}}
isTyping={false}
scrollToBottom={true}
showAvatarForEveryMessage={true}
renderAvatar={() => null}
/>
</View>
);
}
export default Chat;
const styles = StyleSheet.create({
maincontainer: {
flex: 1,
},
});
When axios returns, it usually give the response as res.data, so in your case, try either res.data or res.data.yourToken (I'm not sure how it's your object).
Gurav,
As far as your code above, The api call's will trigger even before you get currentuser or loginToken. You have to handle the api call after getting the currentuser and loginToken. This can be gracefully handled with async, await.
example code:
useEffect(() => {
getData()
}, [])
useEffect(() => {
if(login_token && currentuser) {
//The api call goes here after you get the logintoken andcurrentuser.
// The above condition is just an example but will vary based on your requirements
}
}, [login_token, currentuser])
const getData = async () => {
await getlogintoken()
await getcurrentuser()
}
const getlogintoken = async () => {
await AsyncStorage.getItem("login_token").then(res => {
if (res != null) {
setLoading(false)
setlogin_token(res)
}
else alert("No login token found")
})
}
const getcurrentuser = async () => {
await AsyncStorage.getItem("currentuser").then(res => {
if (res != null) setcurrentuser(res)
else alert("Current user not found")
})
}

Amplify Hub.listen immediately calls signOut after signIn when user put in React Context

I'm having an issue implementing SSO with AWS Amplify into my React Native app. I have SSO working fine, however, when trying to put the cognito user object in my authentication context, Hub.listen calls a signout and the user is immediately signed out.
Here is my code where user is signed up:
SignUp.js
...
useEffect(() => {
const unsubscribe = Hub.listen("auth", ({ payload: { event, data } }) => {
console.log("event", event);
console.log("data", data);
switch (event) {
case "signIn":
console.log("data from signup: ",data)
setUserInContext(data);
break;
case "signOut":
setUserInContext(null);
break;
case "customOAuthState":
setCustomState(data);
}
})
return unsubscribe;
}, []);
...
return (
...
<Button
onPress={() => Auth.federatedSignIn({provider: "Google"})}
>
Google
</Button>
...
)
This code properly opens the google consent screen and allows me to select a user to login with. In fact, if I don't send the returned user object from the to the context like setUserInContext(data) in the above code, I get the user and everything works. However, sending this data to my context seems to cause Hub.listen to detect or invoke the oauth signout, even if I add something like this:
...
const [authUser,setAuthUser] = useState(null)
useEffect(() => {
const unsubscribe = Hub.listen("auth", ({ payload: { event, data } }) => {
console.log("event", event);
console.log("data", data);
switch (event) {
case "signIn":
console.log("data from signup: ",data)
setAuthUser(data);
break;
case "signOut":
setAuthUser(null);
break;
case "customOAuthState":
setCustomState(data);
}
})
return unsubscribe;
}, []);
useEffect(() => {
if (authUser) {
console.log("authuser activated")
loginWithGoogle(authUser)
}
},[authUser])
...
return (
...
<Button
onPress={() => Auth.federatedSignIn({provider: "Google"})}
>
Google
</Button>
...
)
Below is my code for my authentication context:
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
useEffect(() => {
Auth.currentAuthenticatedUser()
.then(user => {
//fetchUserData(user).catch(err => console.error(err));
console.log("currentauthenticateduser: ",user)
setUser(user);
})
.catch((err) => {
console.log("no user found: ",user,"\nerr: ",err)
setUser(null)
});
},[]);
const login = (usernameOrEmail, password) =>
Auth.signIn(usernameOrEmail, password)
.then(cognitoUser => {
setUser(cognitoUser);
return cognitoUser;
})
.catch((err) => {
if (err.code === 'UserNotFoundException') {
err.message = 'Invalid username or password';
}
throw err;
}
);
const loginWithGoogle = (cognitoUser) => {
console.log("setting user: ",cognitoUser)
setUser(cognitoUser)
}
const logout = () =>
console.log("logout called")
Auth.signOut()
.then(data => {
setUser(null);
return data;
}
);
const deleteUser = async () => {
try {
const result = await Auth.deleteUser();
console.log(result);
} catch (err) {
console.log("Error deleting user", err);
} finally {
setUser(null);
}
}
const values = useMemo(() => ({
user,
loginWithGoogle,
login,
logout,
deleteUser
}), [user]);
return (
<UserContext.Provider value={values}>{children}</UserContext.Provider>
)
}
export const useUser = () => {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error('`useUser` must be within a `UserProvider` component');
}
return context;
};
Please send help

Firestore keep loading old changes

I'm trying to create a firestore listener, which handles changes on my collection. After some research, I implement the feature as below.
useEffect(() => {
const firebaseApp = getFirebaseApp();
const db = firestore(firebaseApp);
const handleSnapshotChanges = ( snapshot: FirebaseFirestoreTypes.QuerySnapshot<FirebaseFirestoreTypes.DocumentData> ) => {
const changes = snapshot.docChanges();
changes.forEach((change) => {
if (change.type === "added") {
console.log(change.doc);
console.log(change.type);
}
if (change.type === "modified") {
console.log("Doc modified");
}
if (change.type === "removed") {
console.log("Remove doc");
}
});
};
const query = db.collection("history");
const unsubscribe = query.onSnapshot(handleSnapshotChanges, (err) =>
console.log(err)
);
return () => {
unsubscribe();
};
}, []);
If I doing so, every time I enter the screen where I put the above useEffect, firestore keeps loading all documents in the collection and marks them as added. How can I implement this function properly.

Unable to set state from the response of the api

I have a following function in Api.js
const _getCategories = async () => {
var categories = [];
let token = await getJwt();
var config = {
method: 'get',
url: 'myurl',
headers: {
'Authorization': 'Bearer' + ' ' + token
},
data : ''
};
axios(config)
.then(function (response) {
if(response.status == 200)
{
let res = response.data;
// Create a variable having name in the list.
categories = res.data.apps.map(function(item) {
return {
title: item.name,
color: AppStyles.colorSet.analyticsColor,
lightColor: AppStyles.colorSet.analyticsLightColor,
icon: AppStyles.iconSet.analytics
};
});
// console.log('Returning Categories');
console.log(categories);
return categories;
//console.log(data1)
// Handle and fetch the list of apps
}
else
{
// API didn't go through, generate the error functions
}
})
.catch(function (error) {
console.log(error);
});
};
and I am loading it in homscreen.js
class DashboardScreen extends React.Component {
constructor(props) {
super(props);
const { navigation } = props;
navigation.setOptions({
title: 'Dashboard',
headerLeft: () => (
<TouchableOpacity
onPress={() => {
navigation.openDrawer();
}}
>
<Icon
style={AppStyles.styleSet.menuButton}
name="ios-menu"
size={AppStyles.iconSizeSet.normal}
color={AppStyles.colorSet.mainThemeForegroundColor}
/>
</TouchableOpacity>
),
});
this.state = {
categories: [],
};
}
componentDidMount() {
if (!this.state.data) {
Api.getCategories().then(data => console.log("The data is "+data))
.catch(err => { /*...handle the error...*/});
}
}
onPressCategory = item => {
// Handle onpress for the items
};
render() {
//console.log(this.state.categories);
categoryButtonsRow1 = this.state.categories.map((item, index) => {
if (index < 3) {
return (
<CategoryButton
onPress={() => this.onPressCategory(item)}
color={item.color}
lightColor={item.lightColor}
icon={item.icon}
title={item.title}
/>
);
}
});
return (
<ScrollView style={styles.container}>
<View style={styles.row}>{categoryButtonsRow1}</View>
</ScrollView>
);
}
}
But I am getting category as undefined while printing in render().
I even tried to create an async function in the homescreen.js and call the api with await and set the state after the same but still it is coming as undefined.
Any guesses to what I am doing wrong here. Can anyone help with the same. My best guess is that I am not handling the api request properly.
EDIT
I tried Use Async/Await with Axios in React.js but it is still printing undefined to the same.
The reason for getting undefined is the _getCategories is that its not returning anything and you are chaining using .then to get data so the caller has no way to get this data as a callback is not passed.
You can change the to await like this
const _getCategories = async () => {
var categories = [];
let token = await getJwt();
var config = {
method: 'get',
url: 'myurl',
headers: {
Authorization: 'Bearer' + ' ' + token,
},
data: '',
};
const response = await axios(config);
if (response.status == 200) {
let res = response.data;
// Create a variable having name in the list.
categories = res.data.apps.map(function (item) {
return {
title: item.name,
color: AppStyles.colorSet.analyticsColor,
lightColor: AppStyles.colorSet.analyticsLightColor,
icon: AppStyles.iconSet.analytics,
};
});
// console.log('Returning Categories');
console.log(categories);
return categories;
//console.log(data1)
// Handle and fetch the list of apps
} else {
// API didn't go through, generate the error functions
return null;
}
};
And you can set the state in componentDidMount (should be async)
this.setState({categories:await api._getCategories()});

WebRTC in react-native (hooks), redux - Unhandled Promise Rejections

I'm developing a react-native application, which uses webRTC.
I extremely liked the minimal version I found here (kudos to baconcheese113!) and I decided to refactor it to create my react component.
I have set up a backend (DynamoDB, Appsync) and a redux store that allows me to:
dispatch an action sendCreateUserControlMsg, which down the line calls the Appsync endpoint to create a new ControlUserMsg
subscribe to a ControlUserMsg, set the flag triggerWebrtcData and save webrtcData in the Redux state
The following component (which for now calls itself), sometimes works, but mostly doesn't. I feel that the problem is related to JS Promises, but I do not fully understand how I should design the component to avoid race conditions.
import React, { useState, useEffect } from 'react';
import { View, SafeAreaView, Button, StyleSheet } from 'react-native';
import { RTCPeerConnection, RTCView, mediaDevices } from 'react-native-webrtc';
import { sendCreateUserControlMsg } from '../redux/actions/UserControlMsgActions';
import controlMsgActions from './../model/control_msg_actions';
import webrtcActionTypes from './../model/webrtc_action_types';
import { useDispatch, useSelector } from "react-redux";
import * as triggersMatch from '../redux/actions/TriggersMatchActions';
var IS_LOCAL_USER = true //manual flag I temporarily set
var localUserID = '00';
var localUser = 'localUser'
var remoteUserID = '01';
var remoteUser = 'remoteUser'
if (IS_LOCAL_USER) {
var matchedUserId = remoteUserID
var user_id = localUserID;
var user = localUser
}
else {
var matchedUserId = localUserID
var user_id = remoteUserID;
var user = remoteUser
}
export default function App() {
const dispatch = useDispatch();
var triggersMatchBool = useSelector(state => state.triggers_match)
var webrtcData = useSelector(state => state.webrtc_description.webrtcData)
const [localStream, setLocalStream] = useState();
const [remoteStream, setRemoteStream] = useState();
const [cachedLocalPC, setCachedLocalPC] = useState();
const [cachedRemotePC, setCachedRemotePC] = useState();
const sendICE = (candidate, isLocal) => {
var type
isLocal ? type = webrtcActionTypes["NEW_ICE_CANDIDATE_FROM_LOCAL"] : type = webrtcActionTypes["NEW_ICE_CANDIDATE_FROM_REMOTE"]
var payload = JSON.stringify({
type,
candidate
})
console.log(`Sending ICE to ${matchedUserId}`)
dispatch(sendCreateUserControlMsg(matchedUserId, user_id, user, payload, controlMsgActions["WEBRTC_DATA"]));
}
const sendOffer = (offer) => {
type = webrtcActionTypes["OFFER"]
var payload = JSON.stringify({
type,
offer
})
console.log(`Sending Offer to ${matchedUserId}`)
dispatch(sendCreateUserControlMsg(matchedUserId, user_id, user, payload, controlMsgActions["WEBRTC_DATA"]));
}
const sendAnswer = (answer) => {
type = webrtcActionTypes["ANSWER"]
var payload = JSON.stringify({
type,
answer
})
console.log(`Sending answer to ${matchedUserId}`)
dispatch(sendCreateUserControlMsg(matchedUserId, user_id, user, payload, controlMsgActions["WEBRTC_DATA"]));
}
const [isMuted, setIsMuted] = useState(false);
// START triggers
async function triggerMatchWatcher() {
if (triggersMatchBool.triggerWebrtcData) {
dispatch(triggersMatch.endTriggerWebrtcData());
switch (webrtcData.type) {
case webrtcActionTypes["NEW_ICE_CANDIDATE_FROM_LOCAL"]:
try {
setCachedRemotePC(cachedRemotePC.addIceCandidate(webrtcData.candidate))
} catch (error) {
console.warn('ICE not added')
}
break;
case webrtcActionTypes["NEW_ICE_CANDIDATE_FROM_REMOTE"]:
try {
setCachedLocalPC(cachedLocalPC.addIceCandidate(webrtcData.candidate))
} catch (error) {
console.warn('ICE not added')
}
break;
case webrtcActionTypes["OFFER"]:
console.log('remotePC, setRemoteDescription');
try {
await cachedRemotePC.setRemoteDescription(webrtcData.offer);
console.log('RemotePC, createAnswer');
const answer = await cachedRemotePC.createAnswer();
setCachedRemotePC(cachedRemotePC)
sendAnswer(answer);
} catch (error) {
console.warn(`setRemoteDescription failed ${error}`);
}
case webrtcActionTypes["ANSWER"]:
try {
console.log(`Answer from remotePC: ${webrtcData.answer.sdp}`);
console.log('remotePC, setLocalDescription');
await cachedRemotePC.setLocalDescription(webrtcData.answer);
setCachedRemotePC(cachedRemotePC)
console.log('localPC, setRemoteDescription');
await cachedLocalPC.setRemoteDescription(cachedRemotePC.localDescription);
setCachedLocalPC(cachedLocalPC)
} catch (error) {
console.warn(`setLocalDescription failed ${error}`);
}
}
}
}
useEffect(() => {
triggerMatchWatcher()
}
);
const startLocalStream = async () => {
// isFront will determine if the initial camera should face user or environment
const isFront = true;
const devices = await mediaDevices.enumerateDevices();
const facing = isFront ? 'front' : 'environment';
const videoSourceId = devices.find(device => device.kind === 'videoinput' && device.facing === facing);
const facingMode = isFront ? 'user' : 'environment';
const constraints = {
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30,
},
facingMode,
optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
},
};
const newStream = await mediaDevices.getUserMedia(constraints);
setLocalStream(newStream);
};
const startCall = async () => {
const configuration = { iceServers: [{ url: 'stun:stun.l.google.com:19302' }] };
const localPC = new RTCPeerConnection(configuration);
const remotePC = new RTCPeerConnection(configuration);
localPC.onicecandidate = e => {
try {
console.log('localPC icecandidate:', e.candidate);
if (e.candidate) {
sendICE(e.candidate, true)
}
} catch (err) {
console.error(`Error adding remotePC iceCandidate: ${err}`);
}
};
remotePC.onicecandidate = e => {
try {
console.log('remotePC icecandidate:', e.candidate);
if (e.candidate) {
sendICE(e.candidate, false)
}
} catch (err) {
console.error(`Error adding localPC iceCandidate: ${err}`);
}
};
remotePC.onaddstream = e => {
console.log('remotePC tracking with ', e);
if (e.stream && remoteStream !== e.stream) {
console.log('RemotePC received the stream', e.stream);
setRemoteStream(e.stream);
}
};
localPC.addStream(localStream);
// Not sure whether onnegotiationneeded is needed
// localPC.onnegotiationneeded = async () => {
// try {
// const offer = await localPC.createOffer();
// console.log('Offer from localPC, setLocalDescription');
// await localPC.setLocalDescription(offer);
// sendOffer(localPC.localDescription)
// } catch (err) {
// console.error(err);
// }
// };
try {
const offer = await localPC.createOffer();
console.log('Offer from localPC, setLocalDescription');
await localPC.setLocalDescription(offer);
sendOffer(localPC.localDescription)
} catch (err) {
console.error(err);
}
setCachedLocalPC(localPC);
setCachedRemotePC(remotePC);
};
const switchCamera = () => {
localStream.getVideoTracks().forEach(track => track._switchCamera());
};
const closeStreams = () => {
if (cachedLocalPC) {
cachedLocalPC.removeStream(localStream);
cachedLocalPC.close();
})
}
if (cachedRemotePC) {
cachedRemotePC.removeStream(localStream);
cachedRemotePC.close();
})
}
setLocalStream();
setRemoteStream();
setCachedRemotePC();
setCachedLocalPC();
};
return (
<SafeAreaView style={styles.container}>
{!localStream && <Button title="Click to start stream" onPress={startLocalStream} />}
{localStream && <Button title="Click to start call" onPress={startCall} disabled={!!remoteStream} />}
{localStream && (
<View style={styles.toggleButtons}>
<Button title="Switch camera" onPress={switchCamera} />
</View>
)}
<View style={styles.rtcview}>
{localStream && <RTCView style={styles.rtc} streamURL={localStream.toURL()} />}
</View>
<View style={styles.rtcview}>
{remoteStream && <RTCView style={styles.rtc} streamURL={remoteStream.toURL()} />}
</View>
<Button title="Click to stop call" onPress={closeStreams} disabled={!remoteStream} />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
// omitted
});
The most common errors I receive are:
Error: Failed to add ICE candidate
Possible Unhandled Promise Rejection
and
setLocalDescription failed TypeError: Cannot read property 'sdp' of
undefined
If I console.log I can see that are JS Promise, but since are not a functions I cannot use .then().
How can I call the addIceCandidate method or setLocalDescription method without incurring in the Unhandled Promise Rejection errors?
What are the best practices to work with WebRTC in react-native?