React native fetch api call is not making the request - react-native

I am using an api call to get information for my app which I display to the user. The problem is that when I open the screen for the first time the app displays the information but when I go to a different screen and then comeback I dont see the information unless I restart the app.
This function makes the apiCall for me:
async function getOrders() {
var retrieveData = async () => {
try {
var value = await AsyncStorage.getItem("user");
var data = JSON.parse(value);
return data.user.email;
} catch (error) {
alert(error);
}
};
retrieveData().then((usr) => {
setUser(usr)
fetch(URL + "/api/order/quoted", {
method: "POST",
body: "user=" + usr,
headers: { "Content-type": "application/x-www-form-urlencoded" },
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.error === null) {
setOrders(responseJson.orders);
}
});
});
}
First I use the retriveData function to get the used id, based on that information is server to the user.

You are using react-navigation version 5, so you need to wrap your logic fetch data in useFocusEffect hook react navigation docs
import { useFocusEffect } from '#react-navigation/native';
useFocusEffect(
React.useCallback(() => {
getOrders()
}, [getOrders])
);

The problem can be solved in the following steps:
If you want the data fetched from your endpoint to be used even if you move to other screen use Redux.
If you use redux or not and want to fetch the api every time you open a specific screen then you need to add an onfocus listener. An example is here https://reactnavigation.org/docs/navigation-events/
class Profile extends React.Component {
componentDidMount() {
this._unsubscribe = navigation.addListener('focus', () => {
// do something
});
}

Related

Unable to set useState variable in async method and console log it

Im working with my friends on a app project and we find an issue many times when we tring to set a use state and the console log the variable, i've looking for a solution in the website and saw that the reason is that the usestate is an async awiat which means the variable that i set in the use state isn't immidatly set in it, so i tried many solution that i found in the websites but none of them work for me.
in the screenShot you can see that the json variable is in console log before and the set varaible doesn't show after the setActiveUser , any help?
Thanks!
If you want to do something upon setting state then your best bet is to use the useEffect hook from React and adding your state in the dependency array. This will then call the function in your useEffect every time the state changes. See below a rough example:
import { Text } from 'react-native';
const MyComponent = () => {
const [activeUser, setActiveUser] = useState(null);
useEffect(() => {
// This should log every time activeUser changes
console.log({ activeUser });
}, [activeUser]);
const fetchAuthentication = async user => {
var flag = false;
await fetch('/api/authUser/', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
})
.then(res => {
res.ok && (flag = true);
return res.json();
})
.then(json => {
if (flag) {
setActiveUser(json);
}
})
.catch(error => console.error(error));
return flag;
};
return <Text>Hi</Text>;
};
Full documentation: https://reactjs.org/docs/hooks-effect.html

React-native useEffect hook refresh fetch

I'm trying to build a react-native weather app, fetching data from the openweather api using hooks like this:
useEffect(() => {
async function fetchWeather() {
try {
let data = await fetch(URL);
const json = await data.json();
dispatch({type: 'success', payload: json});
} catch (error) {
dispatch({type: 'failure'});
}
}
fetchWeather();
}, []);
This only loads the data once. I want to make sure the weather info stays up to date. What is the best way to refresh the data? Do I poll every X minutes (if so how)?
Are you looking for a period of time, you will call api?
try it:
const [isStatus, setStatus] = useState(true)
setInterval(()=> {
setStatus(!isStatus)
}, 3000)
useEffect(() => {
fetchWeather();
}, [isStatus])
or you only can use this function:
useEffect(() => {
let loop = setInterval(()=> {
fetchWeather();
}, 3000);
return () => clearInterval(loop);
}, [])
My example applies when the application is opening

React Native, Redux - How to execute async action after successfully executing another async action

In a react native application, I need to update a list item by executing an async action and after successfully executing the particular update async action, I need to reload the list item respectively with the changes of the above update action. Here I'm reloading the list by executing an async action.
I would like to know how to execute two async actions(A and B) sequentially after successfully executing the first one(A) and then the second one (B)
I have implemented a react-native app with redux. Basically it is communicating with an API using web services. I have used Fetch API for implementing async calls and have used a custom implemented Http middleware to handle async calls as a common method(I have not used thunk)
The custom middleware looks like below
export const commonHttpAction = (action) => {
const commonHttpActionTemplate = {
type: '',
urlParam: null,
httpMethod: action.requestMethod == undefined ? 'GET' : action.requestMethod,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken
},
body: action.requestBody == undefined ? undefined : action.requestBody,
payload: null
};
return {
HTTP_ACTION: Object.assign({}, commonHttpActionTemplate, action)
};
};
const httpMiddleware = store => next => action => {
if(action[HTTP_ACTION]) {
const actionInfo = action[HTTP_ACTION];
const fetchOptions = {
method: actionInfo.httpMethod,
headers: actionInfo.headers,
body: actionInfo.body || actionInfo.requestBody || actionInfo.payload || null
};
next({
type: actionInfo.type + "_REQUEST"
});
fetch(getHostUrl() + '/' + actionInfo.urlParam, fetchOptions)
.then(response => response.json())
.then(responseJson => next({
type: actionInfo.type + "_SUCCESS",
payload: responseJson
}))
.catch(error => next({
type: actionInfo.type + "_FAILURE",
payload: error
}));
} else {
return next(action);
}
}
export default httpMiddleware;
Then I have dispatched the async actions through mapDispatchToProps and connect() functions in react-native components/screens using the above custom middleware.
Then reducers will handle the response according to action types.
eg:
ACTION_TYPE_REQUEST, ACTION_TYPE_SUCCESS and ACTION_TYPE_FAILURE
Then in components/screens, I have used "mapStateToProps" function to use the paylod from reducers
As the above-described way, I have fetched data to my screens and Imagine if I have created a Flatlist by dispatching an async action to load the data to the list and I will update one of the list items by dispatching another async action.
I need to re-render the Flatlist after successfully finishing update async action.
So far I have attempted a callback function. but with my implementation, the list loading async action is not dispatching (Simply the Flatlist is not reloading after one of list items updating).
I have written the callback as below
class ExampleComponent extends Component {
componentDidMount() {
this.props.listDataLoadingAction()
}
render() {
return(
<View>
<Flatlist
renderItem={(item) =>
<View>
<TouchableOpacity onPress={() => {this.updateAction,
()=> this.props.listDataLoadingAction()}}>
</TouchableOpacity>
</View>
}
/>
</View>
);
}
updateActon =(callback) => {
this.props.updateListRecordAction();
callback();
}
}
const mapStateToProps = state => {
return{
//get the reducer data
}
}
const mapDispatchToProps = dispatch => {
return {
istDataLoadingAction: () => dispatch(istDataLoadingAction()),
updateListRecordAction: () => dispatch(updateListRecordAction())
}
}
export default connect(mapstateToProps, mapDispatchToProps)(ExampleComponent)
It will be much appreciated if anyone can come up with a solution
It would really help if you had a code snippet of what it is you're trying to do.
In general though you can use async/await
async function () {
await firstAction();
await secondAction();
}
If the first action does not impact the second then I would dispatch and await both
async function () {
await Promise.all([
firstAction(),
secondAction(),
]);
}

Get item from AsyncStorage in React Native

I have a list of companies in React Native.
When I click on one of those companies I get the url of the API that is used for selected company. Then I store it to AsyncStorage and then I show the login screen. The function is as follows:
selectCompany(data_url, e) {
AsyncStorage.setItem("data_url", JSON.stringify(data_url), () => this.props.login());
}
Then on login page if I click on sign in button I go to the onLogin function, the function is as follows:
onLogin: function() {
fetch(data.url + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
},
And data.url comes from data.js file, and I try to get url from the data.js file as follows:
let data_url = AsyncStorage.getItem("data_url").then(json => JSON.parse(json));
module.exports = {
url: data_url,
.....
}
But it doesn't work. Any advice?
AsyncStorage is async, therefore data_url will not be defined until it's retrieved what its looking for, you would need to move the fetch into the promise thats returned from the get so it will run it once it's done getting the data. This might be one way you tackle it:
const data_url = () => AsyncStorage.getItem("data_url"); //change this into a function
module.exports = {
url: data_url,
.....
}
now inside your component...
onLogin: function() {
data.url().then((url) => {
fetch(JSON.parse(url) + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
});
},
AsyncStorage.getItem is a promise and needs to await for response rather than accessing direct and the function calling it should be defined as async. Here is an example to retrieve from AsyncStorage..
export async function getAccessKey(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
return accessToken;
}

React Native Pass data to another screen

I need to pass some data from one screen to another, but I don't know how to do it. I've searched and I read about Redux, but it is a bit complicated since I never used it and most of the tutorials are confusing for a newcomer. But if I could do it without Redux, that would be better.
So, when I click in a button, It runs this:
onSearch() {
var listaCarros = fetch(`URL`, {
method: 'GET',
})
.then((response) => { return response.json() } )
.then((responseJson) => {
console.log(responseJson)
})
}
and I want to pass the data I get from this, to another screen.
Im using router-flux, if that matters.
you can save the response in state of your current component like
onSearch() {
var listaCarros = fetch(`URL`, {
method: 'GET',
})
.then((response) => { return response.json() } )
.then((responseJson) => {
console.log(responseJson);
/*for react-native-router-flux you can simply do
Actions.secondPage({data:responseJson}); and you will get data at SecondPage in props
*/
this.setState({
dataToPass :responseJson
});
})
}
then below in return like you want to pass data to a new component having named as SecondPage, you can do it in following way
render(){
return(
{this.state.dataToPass && <SecondPage data ={this.state.dataToPass}>} //you will get data as props in your second page
);
}