React Native: setState delay - react-native

I'm trying to set a state from a fetch response, but it seems it takes a while to update the state.
What I learned is that fetch is quick until it comes to setState. There, it takes about 3 seconds to update.
fetch(ENDPOINT)
.then((response) => response.json())
.then((responseJson) => {
this.setState({product : responseJson.product, related: responseJson.related, ready: true});
})
.catch((error) => {
console.error(error);
}).done();
Any tips?
Thanks

setState is asynchronous.
From the docs of react itself if you see :-
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
You can read more about this in the documentation.
Also, you can check something similar here

Related

How to use useEffect() without performance degradation

I have a React Native app where, on one screen, I have a useEffect hook that constantly fetches data from a site and updates my local variable with that data (see below for a simplified example). The problem I am running into though is that this is killing the performance of my app. Even if you leave this screen, the app is sluggish.
The issue is no doubt caused by the countless calls to the URL to get the data & constantly resetting my local variable with the data. I tried using a dependency array with the hook, but if I do that it doesn't continually update, it only updates on the first load of the screen and I need it to update whenever there is a change (whenever new data is available).
Is there a way to do this so that I constantly get any updates from the remote source but don't slow down my app?
const [retrievedData, setRetrievedData] = [{}];
useEffect(() => {
let fetchedData;
fetch(
'https://site-with-data.com',
)
.then(response => response.json())
.then(json => {
setRetrievedData(processData(json.events));
})
.catch(error => console.error(error));
});
When useEffect doesn't have a dependencies array (the second argument), it will run on every render. That is why you are making so many calls.
If you need to update the data real-time, the first question you must answer is: how will the client know the data has changed in the server?
One way you could make the server notify the client is by using WebSockets. A WebSocket creates a bidirectional connection between the client and the server, and the server can notify the client whenever anything changes. Other than that, you could also use a technique called "long polling" or "server-sent events".
But any of these three solutions would require you to change your server (in addition to your client).
Quick fix: limit the update frequency to "refresh every N seconds"
The quick and dirty alternative without changing the server is just to decide a frequency (e.g., update every 5s) and go with it in the client.
Example:
useEffect(() => {
const intervalId = setInterval(() => {
fetch(
'https://site-with-data.com',
)
.then(response => response.json())
.then(json => {
setRetrievedData(processData(json.events));
})
.catch(error => console.error(error));
}, 5000); // update every 5s
return () => clearInterval(intervalId);
}, []); // add to this array any variable that affects the fetch (e.g., URL)
Which number of seconds should you use? That will depend on each case. To decide that, evaluate both UX and server load.

Making multiple api requests at once using fetch in vue

I would like to make two api call's at once to a ReST API in my vue component. I have done research online and am using this logic:
// Multiple fetches
Promise.all([
fetch(
`https://api.covid19api.com/live/country/${this.selected}/status/confirmed/date/${this.yesterday}`
),
fetch(
`https://api.covid19api.com/live/country/south-africa/status/confirmed/date/2020-03-21T13:13:30Z`
)
])
.then(responses => {
// Get a JSON object from each of the responses
return responses.map(response => {
return response.json();
});
})
.then(data => {
// Log the data to the console
// You would do something with both sets of data here
this.coronaVirusStats1 = data[0];
console.log(this.coronaVirusStats1);
})
.catch(function(error) {
// if there's an error, log it
console.log(error);
});
}
The consoled value is a promise which I understand but when I look in the Vue devTools under my component I see that coronaVirusStats1 has a value of "Promise", not the array of objects I expect back. When I do a single fetch and consume the data variable there is no problem. However I am perplexed as to how one accesses the returned data from fetch calls to multiple endpoints. I tried all the solutions here fetching api's ,but none worked. If someone can elucidate on the proper way to access the data from the fetches I would be most appreciative.
You're just about there. The issue is that your first then returns an array of promises. Unfortunately, promise chains only work with a Promise instance so there's nothing here that will wait for your promises to resolve.
The quick fix is to change the first then to
return Promise.all(responses.map(r => r.json()))
That being said, there's a little more to the fetch API, particularly for dealing with errors.
I would use something like the following for each fetch call to make sure network errors and non-successful HTTP requests are handled correctly.
This will also handle unwrapping the JSON response so you don't have to use the above
Promise.all([
fetch(url1).then(res => res.ok && res.json() || Promise.reject(res)),
fetch(url2).then(res => res.ok && res.json() || Promise.reject(res))
]).then(data => {
// handle data array here
})
See https://developer.mozilla.org/en-US/docs/Web/API/Response/ok

API response is not accessible in ComponentDidMount but in render I can use

Hi I am working on React Native app. I am using Redux and Saga. I call the API in componentDidMount.
async componentDidMount() {
let data = this.props.navigation.getParam("returnProductData");
if (data) {
console.log("Return Here");
this.props.getProductReturnAction(data)
this.setState({
returnQty:parseInt(this.props.product.item_ordered)-parseInt(this.props.product.already_return_qty)
});
console.log(this.state.returnQty,"Return quty"); //coming undefined
console.log(this.props.product, "product"); // undefined
console.log(this.props.product.item_ordered); //undefined
}
}
I have to set the state in componentDidMount for returnQty. But, state is not accessible here. It's working fine in render method. I can use all the product object. But, it is coming empty in componentDidMount. I tried using async and await but it's not working.
// Dispatch Methods
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{ getProductReturnAction, submitProductReturnAction },
dispatch
);
};
// Props
const mapStateToProps = state => {
return {
product: state.myOrdersReducer.returnProduct
};
};
I can't be able to find out the bug please help to find out the best solution.
When you are making API calls through redux/saga, you can not use async await, as the frameworks will just dispatch an action and return back, the listeners which are registered for the action will be triggered and then after they complete their work they will dispatch a new action and respect reducer will handle the response.
Explained above is general scenario.
In your scenario,
You are dispatching the action returned by getProductReturnAction which will give say GET_PRODUCTS action.
A saga would be registered for GET_PRODUCTS, say getProducts, this get invoked.
This will perform the API call once the response is received it will dispatch GET_PRODUCTS_SUCCESS along with the products data.
Corresponding reducer which handles GET_PRODUCTS_SUCCESS will get called and that updates returnProduct and as you are registered for that in your component the render method gets called (as the props are changed) and hence product data is available in your render method.
This is working perfectly correct. I don't see anything wrong here.
As the data is available in props use the same u do not need to do a setState again on that.

Handling 401 responses in a react-native functional component

I'm getting the following warning when an API request returns 401 and I send the user to the login screen:
Warning: Can't perform a React state update on an unmounted component.
What is the best way to handle this warning in a functional component that uses hooks. See the code below:
.
.
export default function MovieDetailsScreen() {
const [movie, setMovie] = useState({});
const movieId = useNavigationParam('movieId');
useEffect(() => {
// This is the method that does the request and returns 401 (It
// uses the fetch library)
Client.movies.show(movieId)
.then(result => {
setMovie(result)
})
}, [])
.
.
.
In general, warnings don't crash your application. But you should care about them. For instance, the previous warning(s) can lead to performance issues when not properly unmounting your stateful components
the request (e.g. Promise) isn't resolved yet, but you unmount the component. Then the request resolves, setMovie() is called to set the new state, but it hits an unmounted component.
You can try catch
Client.movies.show(movieId)
.then(result => {
setMovie(result)
})
.catch((err) => {
Client.movies.stop()
})

Redux Thunk vs Making api call in react component

I was wondering if what I've been doing in my ReactNative/Redux application is wrong. This is how I've been handling async actions.
MyComponent.js
componentDidMount() {
fetch('https://www.mywebsite.com')
.then(data => this.props.handleApiSuccess(data)) // injected as props by Redux
.catch(err => this.props.handleApiError(err)); // injected as props by Redux
}
The redux-thunk way I should probably be doing
export const handleApiCall = () => dispatch => {
fetch('https://www.mywebsite.com')
.then(data => dispatch(handleApiSuccess(data)))
.catch(err => dispatch(handleApiError(err)));
}
Is there anything wrong with the way its being done in the first part?
There is nothing wrong in terms of bugs: the code will work and serve its purpose.
But in terms of design it has huge flaw: coupling. Merging your fetching logic inside the Components may cause complications as your app will grow.
What if your way of fetching will change, e.g. you'll decide to communicate with server via websockets? What if your way of handling of server response will change, i.e. handleApiError will have to be replaced with something else? What if you'll decide to reuse same fetching in the different part of your app?
In all these cases you'll have to alter your existing Components which ideally shouldn't be affected by such logic changes.