I want to persist the fetched API data (Array) when reopening a React Native app without internet connection.
Fetching and storing the data in state and AsyncStorage works just fine using Axios.
When opening the app without internet connection, there is an endless load time, I cannot figure out how to trigger the catch promise and use my stored data as the new data for the state.
This is my code:
componentWillMount(){
axios.get('http://example.com/api_get_request')
.then((response) => {
console.log('connection ok!');
AsyncStorage.setItem('landen', JSON.stringify(response.data));
this.setState({
landen: response.data,
loading: false,
});
//DATA SET TO STATE + ASYNCSTORAGE
})
.catch(error => {
console.log(error.response);
AsyncStorage.getItem('landen').then((value) => {
this.setState({
landen: JSON.parse(value),
loading: false,
});
//NO CONNECTION -> ASYNC DATA
});
});
}
You can use NetInfo to check internet connection and as per connection execute your code like below.
componentWillMount() {
NetInfo.isConnected.fetch().then(isConnected => {
if (isConnected) {
axios.get('http://example.com/api_get_request')
.then((response) => {
AsyncStorage.setItem('landen', JSON.stringify(response.data));
this.setState({
landen: response.data,
loading: false,
});
})
} else {
AsyncStorage.getItem('landen').then((value) => {
this.setState({
landen: JSON.parse(value),
loading: false,
});
});
}
});
}
Please note, above code is not tested and you may have to fix few things here and there but you will get the concept. Also, you may have to add catch block to catch unexpected errors.
For manage some little info is a good idea use AsyncStorage. But if you need manage all your app states, it's a good practice passing your code to Redux and use libraries like:
https://github.com/rt2zz/redux-persist
https://github.com/redux-offline/redux-offline
Related
I'm using the async fetch hook to render the component in SSR and making the API call inside the same. But on the live server, it is actually loading one more time on the client-side and making one more call to API and as API isn't exposed during client-side so data just washes away and the array object gets again set to empty.
data() {
return {
errored: false,
randomData: [],
};
},
async fetch() {
await this.$axios
.get(this.routeUrl)
.then((res) => {
if (res.data.length == 0 || res.status != 200) this.errored = true;
this.randomData = res.data;
})
.catch((err) => {
console.log(err);
});
},
fetchOnServer: true,
I want to persist this randomData variable, so there shouldn't be another call on the client-side to populate the same.
If you want to pass some data from the server-side into the client-side, you could use the nuxtServerInit Vuex action
/store/index.js
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}
So, the issue was that, as I was Docker and the API was only exposed to the server-side and not the client-side. For a blink, the content was available and then just all gone. Because the async fetch and asyncData hooks are called on the server and as well as on the client-side. I solved it using the Vuex store. Now I started storing the data fetched on the server in store and using the same on the client-side. Here is how I implemented that:
// store/index.js
export const state = () => ({
randomData: [],
});
export const mutations = {
SET_RANDOMDATA: (state, payload) => {
state.randomData = payload;
},
};
// Inside the component
// Fetch the data from API on server-side
async fetch() {
if (process.server) {
await this.$axios
.get(this.routeUrl)
.then((res) => {
this.$store.commit("SET_RANDOMDATA", res.data);
})
.catch((err) => {
console.log(err);
});
}
},
computed: {
...mapState(["randomData"]),
},
fetchOnServer: true,
I am pulling the data from MongoDB database using axios and set the value to a state value named invoices
I do this in componentDidMount. And again I want to access that state (i.e. invoices) within the componentDidMount. I am trying to set invoices value to another state value called dataa. But always ends up getting empty/null.
Problem is the state has Not been set, value is empty
This is what my code snippet looks like:
componentDidMount() {
axios
.get("http://localhost:4005/purchaseinvoices")
.then(response => {
this.setState({
invoices: response.data //setting value to invoices
});
})
.then(
this.setState({
dataa: this.state.invoices //setting value to dataa
})
)
.catch(err => {
console.log(err);
});
//but this gives 0
alert(this.state.invoices.length)
}
what is the possible cause of problem and how can I fix this?
Update the data directly from response instead of using this.state.invoices.since this.setState is an async call,takes some ms delay to update the state.
componentDidMount() {
axios
.get("http://localhost:4005/purchaseinvoices")
.then(response => {
this.setState({
invoices: response.data //setting value to invoices
data: response.data
});
})
.catch(err => {
console.log(err);
});
}
If you want to see the data on first load of page ,try server side rendering
That's because axios and setState are asynchronous.
But, you can see updated stated in componentDidUpdate or in render function.
Edit: Also you can access just the stated is updated like below;
axios
.get("http://localhost:4005/purchaseinvoices")
.then(response => {
this.setState({
invoices: response.data, //setting value to invoices
dataa: response.data
}, () => {
alert(this.state.invoices.length)
});
})
.catch(err => {
console.log(err);
});
}
In compoenntDidMount lifecycle, I am fetching an API, getting data and catching the potential error. I can get the data properly. However, In catching the error stage, I would like to update my state as well but so weird, I cannot.
In state I have an isError boolean. It is false by default. When I change the api url in fetch, I can see console.log message, but my isError is still false.
componentDidMount() {
fetch(
"url"
)
.then(response => response.json())
.then(data => {
this.setState({
data1: data.response.venues,
});
})
.catch(error => {
this.setState({isError: true})
console.log("bla bla", error)
});
}
Nicholas Tower
is right i think.
First are you using ES6 or redux ?
Secondly try to pass your api call into an async function.
Catch the call with await to avoid setState execution as nicholas said.
If you use redux call your api with an action is a better practice.
A little example here : https://redux.js.org/advanced/asyncactions
maybe like that :
{
try {
const ret = await my_action(*);
}
catch (error) {
this.setState({
isError: true,
errorInState: error,
});
}
}
Sometimes the speed of execution surpasses some slower actions and creates creates incredible bugs.
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})
})
}
Below is my connection request code :
doLogin(this.login).then(response => {
var token = response.data.token;
localStorage.setItem('AUTH_TOKEN', JSON.stringify(token));
this.$router.push({name: 'Devices'});
});
}).catch(error => {
console.log(error.response.data.message);
});
the catch() part works fine for http errors (like 400, 404, 403..etc). But when my server is offline this script just throws net::ERR_CONNECTION_REFUSED. Is there any way to handle this error and let the front-end user know that the server is currently offline.?
Here is the doLogin() function just in case,
function doLogin(data) {
const url = 'http://localhost:3000/authenticate';
return axios.post(url,data);
}
You can try this in the catch part:
catch(error => {
if (!error.response) {
// network error
this.errorStatus = 'Error: Network Error';
} else {
this.errorStatus = error.response.data.message;
}
})
You may use interceptors:
axios.interceptors.response.use(
response => {
return response
},
error => {
if (!error.response) {
console.log("Please check your internet connection.");
}
return Promise.reject(error)
}
)
You should do the same validation that #chithra pointed out in the .then() because i'm having a weird issue when testing requests with my servers down, the "response" comes as if it was a success.
Also, on the .then() do it with response.status instead of response.error
By using npm; a standard package manager for the JavaScript runtime environment Node.js.
With npm:
npm i axios
Next, you should import Axios in your src/App.vue file
import axios from 'axios';
you will need to call it on a lifecycle hook. Here we will use the beforeCreated() lifecycle hook, this is because we will be able to retrieve sensitive data and events that are active with the beforeCreated hook.
data() {
return {
network: false,
}; },
beforeCreate() {
axios
.get("http://localhost:13172/api/product/getproducts")
.then((response) => {
console.log(response);
this.network = true;
})
.catch((error) => {
console.log(error), (this.network = false);
}); }