I'm using firebase to build an app with react native. I'm struggling with how I should structure my redux actions within my app.
As is stands I have a function that will fetch a users profile. I make a reference to the firebase firestore and then use then and catch statements to resolve the promise returned. Here is where my problem comes into it.
return(dispatch){
dispatch({type: GET_PROFILE})
var docRef = firebase.firestore().doc('users/' + userUid);
docRef.get()
.then(() => {
dispatch({type: GET_PROFILE_SUCCESS})
})
.catch(() => {
dispatch({type: GET_PROFILE_FAIL})
})
}
I am dispatching the action GET_PROFILE so that when my render function is called in my profile screen, it will load a spinner while the user waits.
The problem I've got is that if the user isn't connected to the internet. When they make the request, the action is sent to redux but GET PROFILE SUCCESS / FAIL is never dispatched, as having no internet connection isn't an error
As a result all the user sees is a spinner until they restart the app. I basically want to alert the user that they need a connection to the internet.
I'm new to react native, Could someone advise me what's the best way to do this in react native?
Thanks
You can get network informations with NetInfo and store it in redux :
In your main component :
componentDidMount() {
NetInfo.isConnected.fetch().then((isConnected) => {
store.dispatch({ type: 'CHANGE_CONNECTION_STATUS', isConnected });
});
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectionChange);
}
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectionChange);
handleConnectionChange = (isConnected: boolean) => {
store.dispatch({ type: 'CHANGE_CONNECTION_STATUS', isConnected }));
}
Then you can wrap your request with an if statement to handle offline use cases :
return(dispatch){
if(store.getState().isConnected) {
dispatch({type: GET_PROFILE})
var docRef = firebase.firestore().doc('users/' + userUid);
docRef.get()
.then(() => {
dispatch({type: GET_PROFILE_SUCCESS})
})
.catch(() => {
dispatch({type: GET_PROFILE_FAIL})
})
} else {
Alert.alert('You are offline');
}
}
You can use this piece of code for network fail condition
return(dispatch){
dispatch({type: GET_PROFILE})
var docRef = firebase.firestore().doc('users/' + userUid);
docRef.get()
.then(() => {
dispatch({type: GET_PROFILE_SUCCESS})
},
// ADD THESE LINES
(error) => {
dispatch({type: GET_PROFILE_FAIL})
})
.catch(() => {
dispatch({type: GET_PROFILE_FAIL})
})
}
Related
I have
useEffect(() => {
setLoading(true);
axios
.get(url, {params})
.then(data => {
setData(data || []);
setLoading(false);
})
.catch(() => {
showToast('Load data failed!', 'error');
setLoading(false);
});
}, [params]);
It gives me
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Ok, the question IS NOT HOW TO SOLVE IT. When I use setLoading(false) after axios promise it works fine but inside of promise (e.g. above) it always gives me warning. Actually I want to know WHY IT HAPPENS SO? Is there anybody who may explain me in a nutshell a flow of code above (the process how code above works with warning) and maybe give some best practices on using hooks.
you need clean up function.
this means you should call function end of useEffect function.
when dependencie is changes (params as your example ) calls that function.
so we would be able controll when component mounts/unmounts
useEffect(() => {
let cancelled = false;
setLoading(false);
async function fetchData() {
try {
const response = await axios.get(url, { params });
if (!cancelled) {
setData(response.data);
setLoading(false);
}
} catch (e) {
if (!cancelled) {
showToast(e.message, "error");
setLoading(false);
}
}
}
fetchData();
// clean up here
return () => {
cancelled = true;
};
}, [params]);
WHY IT HAPPENS SO?
Imagine your request is goes slow, and the component has already unmounted when the async request finishes. this time throws this warning
I'm using firebase .signInWithEmailAndPassword(email, password).then() for authentication in a react-native Android project.
I have the function called on an onPress button event. The authentication does take place but for some reason, the .then() does not fire unless I tap somewhere else on the screen. It will happily wait 5 mins until I tap somewhere other than the button to fire.
I can see that the auth is taking place. It's just the .then() promise that hangs until focus is shifted away from the button.
I'm using react-native 0.59.5 and firebase 5.1.0 node libraries. I've tried console.logging each step and it's clear the then() is where it fails. Strangely catch() works immediately.
export const loginUser = ({ email, password }) => {
return dispatch => {
dispatch({ type: LOGIN_USER })
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(loginUserFail(dispatch))
})
}
}
const loginUserFail = dispatch => {
dispatch({ type: LOGIN_USER_FAIL })
}
const loginUserSuccess = (dispatch, user) => {
console.log('Firing success')
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
})
}
In the above example, loginUserFail will run immediately if auth fails but loginUserSuccess will wait indefinitely until I tap somewhere else in the application.
Do you have your remote debugger open in a Chrome browsers?
Close it (debugger), reload app in simulator and it will work as expected.
Just stop the Remote Debugger in your application, hope this will help
try to remove the "then" promisse:
firebase.auth().signInWithEmailAndPassword(email, password)
.catch(error => {
dispatch(loginUserFail(error));
});
After that try to create an action with this comand:
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log('success sign in');
dispatch(loginUserSuccess(user));
} else {
// No user is signed in.
}
});
I'm building a PWA using the Vue CLI 3 PWA plugin, and I'm not sure how to handle the response from an AJAX call made while offline.
All I have is basically sample code from the Workbox documentation...
// service-worker.js
const queue = new workbox.backgroundSync.Queue('CR_OfflineQueue')
self.addEventListener('fetch', (event) => {
const promiseChain = fetch(event.request.clone())
.catch((err) => {
console.log('Queued Event:', event.request)
return queue.addRequest(event.request)
})
event.waitUntil(promiseChain)
})
The AJAX is just a basic Axios get() that's called when the store is loaded and in a test mutation
// store.js
getUsername () {
return axios.get(
'http://localhost:8005/username/',
{
responseType: 'json',
withCredentials: true
})
}
const store = new Vuex.Store({
state: {
username: null
},
mutations: {
test_api (state) {
getUsername()
.then((res) => {
state.username = res.username
})
.catch((err) => {
console.error('getUsername:', err)
})
}
}
})
The request is being successfully replayed, but I have no idea what to do with it, and I'm unable to find an example using workbox.backgroundSync.Queue.
As far as getUsername() is concerned, the request failed, but how do I have the calling function essentially pick up where it left off?
I don't think that need something to check the queue for the request at a regular interval to "manually" re-trigger (not via the auto replay). How would I use the queue class to take the result from the replayed request and set the username property?
I got Users and Places as two different databases where each user has may places.
I have BasicInfo component where at a time 1 place of the user has to be loaded.
ComponentWillMount of BasicInfo
componentWillMount() {
this.props.dispatch(fetchUser())
this.props.dispatch(fetchPlaces(1))
}
I need to pass user id in fetchPlaces, as of now I'm hardcoding, but how do I do with the results of fetchUser? Should I do in componentWillReceiveProps(nextProps) ? Or is there any other way of doing it?
componenetWillReceiveProps makes sense, but I was wondering what if it is a chain of events? May be depending on place id if I have to fetch some other data.
Actions :
export function fetchPlaces(user_id) {
return function(dispatch) {
axios.get("/getPlaces?user_id=" + user_id)
.then((response) => {
console.log(response);
dispatch({type: "FETCH_PLACES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_PLACES_REJECTED", payload: err})
})
}
}
export function fetchPlace(place_id) {
return function(dispatch) {
axios.get("/getPlace?place_id=" + place_id)
.then((response) => {
console.log(response);
dispatch({type: "FETCH_PLACES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_PLACES_REJECTED", payload: err})
})
}
}
It looks like you're already using redux-thunk? If so, you can return the promise from your axios.get() calls:
return axios.get(...);
...then you can do something like this (I'm guessing what your fetchUser and user might look like):
this.props.dispatch(fetchUser()).then(user => this.props.dispatch(fetchPlaces(user.id)))
You can use a thunk or a promise middleware. You can find the motivation and examples in the documentation.
I am using Fetch API for requests and I want to handle an error when internet is disabled. But I didn't get any response in THEN function and no error message in CATCH function. How can I handle this issue. I am trying do thid in React Native. Thank you before hand
Checkout the NetInfo API available with React Native. You should be able to use the isConnected method to check if it's connected to the internet before making a request.
import {NetInfo} from 'react-native'
NetInfo.isConnected.fetch().then(isConnected => {
console.log('First, is ' + (isConnected ? 'online' : 'offline'));
if (isConnected) {
fetch(...)
}
});
I was able to replicate the catch never getting called when the WiFi is off. Implementing a solution like this to manually throw an error if the request never returns could be a way around this. See this discussion on handling Timeouts with Fetch.
let test = () => {
return new Promise((resolve, reject) => {
let id = setTimeout(() => reject('Timeout'),3000)
fetch('https://swapi.co/api/people/1/')
.then(res => {
resolve(res)
clearTimeout(id)
})
.catch(err => {
reject(err)
clearTimeout(id)
})
})
}
test()
.then(() => console.log('************Success'))
.catch(() => console.warn('*************Error'))