React Native - seeing stuff load - react-native

This might be kinda an awkward question as I am not sure the best way to phrase this..
My React Native app does a decent amount of pulling from a server.. its a social app, sometimes when I load a page in the app that is pulling a decent amount of data from a server you can kinda see stuff loading in.. like almost kinda slowly
Is their a solution to this, maybe a way so it dosen't even render the page until its completely done loading? Or is the solution some sort of loading animation? Any help would be awesome

I believe this is a UX problem and therefore needs a solution such as an activity indicator or overlay with a loading message. Also like Facebook's newsfeed approach, you can display default empty cards so as to make users know that the page is loading.

An easy solution to avoid premature content flashing is to render the content only after fetching is completed. Below is an example component demonstrating this idea.
class YourComponent extends React.Component {
static propTypes = {
fetchData: React.PropTypes.func.isRequired,
}
constructor(props) {
super(props);
this.state = {
dataFetched: false
};
}
async componentDidMount() {
await this.props.fetchData();
this.setState({dataFetched: true});
}
render() {
return (
<View>
{this.state.dataFetched ?
<Text>Fetched</Text> :
null
}
</View>
);
}
}

Related

Low performance on React Native maps when components are used inside of marker

I am making a react native map, using MapView from 'react-native-maps' and this marker clustering engine. This is the component for a cluster marker that has been working just fine with blazing performance:
export default class ClusterMarker extends PureComponent {
constructor(props) {
super(props)
this.state = {
tracksViewChanges: true
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props !== nextProps) {
this.setState(() => ({
tracksViewChanges: true,
}))
}
}
componentDidUpdate() {
if (this.state.tracksViewChanges) {
this.setState(() => ({
tracksViewChanges: false,
}))
}
}
render() {
/*...
bunch of unrelated code where 'message', 'image', 'coordinate' and 'pointCount' are set
...*/
return (
<Marker
anchor={{x:0.5,y:0.5}}
centerOffset={{x:0.5,y:0.5}}
coordinate={coordinate}
image={image}
title={pointCount}
description={message}
tracksViewChanges={this.state.tracksViewChanges}>
{/* <Text>{pointCount}</Text> */} <-------- I want this to work just as fast
</Marker>
)
}
}
See that title={pointCount} in Marker props? When a user clicks a cluster, an overlay pops up to show them how many pins are there in the cluster. I want to bring that text out of there and display it over the cluster. Note that in render inside Marker, there is a commented line. When uncommented, visually it does pretty much what is needed, but with a terrible perfomance on phone (1 fps would be an overstatement).
It's clear that the problem is with Text. My hypothesis is that the Text component keeps on checking parent if there are updates on text, or that it re-renders all the time for no reason. Here is a list of things I've tried to fix this:
Create a component extending from Text, performing the same task without weird updates or re-renders. No fps increase.
Create a component that renders a Text but never updates or re-renders for no reason. No fps increase.
Add the tracksViewChanges logic you can see above. That was somewhat of a success, increasing average fps from 1 to 5.
Use other marker clustering libs (all of which failed to work with decent performance even without the Text inside Marker).
Many hacky solutions from the internet, none of which actually improved performace.
This map is currently dealing with an average of 20,000 pins and the solution has to be performant as this pin count increases, because it will.
I would be very glad if anyone could help!
Perhaps this is not the answer you are looking for, but 20k markers is... a lot. Have you considered clustering the markers based on the zoom level? You could also filter out markers that are not in the map viewport.

react native Webview Process Terminated

Im using react native to show a local HTML on a web view,
I have noticed that some times randomly, the webView crashes, and I get the message on console:
Webview Process Terminated
I have searched about it, but cannot find any answers?
what is that? why is happening? how to avoid it or reload web view after that error?
<WebView
style={styles.webContainer}
originWhitelist={['*']}
source={isAndroid ? {uri:'file:///android_asset/binaura.html'} : HTML_FILE}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
If by "WebView" you mean react-native-webview:
This happens on iOS if the WebView is using too many resources, and results in either the WebView crashing (showing a blank white page) or the app freezing.
There is a callback property, onContentProcessDidTerminate (introduced in this PR), that you can use to listen for termination and force a reload of the WebView by updating its key. It's a bit of a hack, but it works.
Usage example:
class MyComponent extends Component {
state = {
webviewKey: 0,
}
reload() {
this.setState({
webviewKey: this.state.webviewKey + 1,
})
}
render() {
return (
<WebView
key={this.state.webviewKey}
onContentProcessDidTerminate={this.reload}
/>
)
}
}
#pjivers answer put us on the right track but the current react-native-webview already has a reload() method you can use. You only need a bit of ref setup to call it:
class MyComponent extends Component {
constructor(props) {
super(props);
this.webViewRef = React.createRef();
}
render() {
return (
<WebView
ref={this.webViewRef}
onContentProcessDidTerminate={this.webViewRef.current?.reload}
/>
)
}
}

Localization of React Native navigators

I am building an app where the users are prompted to choose their language the first time they launch the app. The language is then stored locally using AsyncStorage.
Every time the app launches the language is retrieved from storage and saved into the global.lang variable to be used by all components:
AsyncStorage.getItem('settings', (err, item) => {
global.lang = item.lang;
});
When I use the global.lang variable in the render() method in any component everything seems to be ok. However I run into trouble when trying to use the same variable when initializing my navigators:
const TabNavigator = createBottomTabNavigator(
{
Home: {
screen: HomeScreenNavigator,
navigationOptions:{
title: strings['en'].linkHome, --> this works
}
},
News: {
screen: NewsScreen,
navigationOptions:{
title: strings[global.lang].linkNews, --> this fails
}
}
});
I believe that this because the value is not retrieved from AsyncStorage by the time that the navigators are constructed. If I set the global.lang manually (eg. global.lang = 'en';) it seems to be OK, but not when I try to retrieve it from the storage.
Is there something that I am missing? Could I initialize the navigator with a default language and change the title later based on the value retrived?
Any help would be greatly appreciated.
The navigators are constructed in the app launch. So you would need to use some placeholder text and use the method described here where you change all screen titles based on the screen key...
Or... this sounds insane and i have never tried it. But you can use a loading screen where you retrieve the languaje settings. then... via conditional rendering you "render" a navigator component . Idk if it would work the same way , but you can try it. below some code that i just created for this purpose
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = { hasLanguage:false};}
componentDidMount(){
this.retrieveLanguage()
}
async retrieveLanguage(){
//await AsyncStorage bla bla bla
//then
this.setState({hasLanguage:true})
}
render() {
return (
{
this.state.hasLanguage?
<View>
//this is a view that is rendered as a loading screen
</View>:
<Navigator/>//this will be rendered, and hence, created, when there is a language retrieved
}
);
}
}
Again. I don't know if react navigation creates the navigator at render . If so. When it creates a navigator , there should be the languaje to be used there

React Native - Activity Indicator while FlatList loads images

Whenever my app mounts, I have set:
constructor(props) {
super(props);
this.state = {
loading: true
};
}
I have an activity indicator which animates until loading: false:
{this.state.loading &&
<View style={styles.loading}>
<ActivityIndicator color="red" animating={this.state.loading} />
</View>
}
I tried searching inside FlatList's documentation for a method would tell me if all items have been rendered so I can call this.setState({loading: false}); However, I did not manage to find such method.
Does anyone know how can I display my activity indicator whilst the list is loading its data?
Your problem is not react native related. Which is why you couldn't find any help regarding it in the react native documentation.
Assuming that your data is coming in asynchronously and in reference to your question you are not able to figure out when this asynchronous operation ends.
Use promises or async await or any other functionality like that, to figure out when this asynchronous operation ends and then use setState to disable the activity indicator and then show the flatList.
Let me know if you need more explanation or clarity on the above said.

Set updated state props on button click using redux

I am trying to create a button which is displaying and hiding a navigation menu on click. I am using Redux to get the current state into the Component, but something is not working with the onPress function.
When pressing the button I want to check the current state of this.state.showNavigation (can be true/false) but I am getting an "undefined is not an object" error immediately after clicking the button.
I think I am running into a lifecycle issue here. I already tried to ship around this via setting the state in componentWillMount like that:
componentWillUpdate(){
this.state = NavigationStore.getState();
}
Anyway that didn't help. Some advise is much appreciated. Thanks!
Heres my code:
class NavigationButton extends React.Component {
constructor(props, context) {
super(props, context);
this.state = NavigationStore.getState();
NavigationStore.subscribe(() => {
this.setState(NavigationStore.getState());
});
// alert(this.state.showNavigation);
}
render() {
return (
<TouchableOpacity
onPress={this.handlePressButton}
style={navigationButtonStyles.button}>
<Image source={buttonImage} />
</TouchableOpacity>
);
}
handlePressButton() {
if(this.state.showNavigation){
NavigationStore.dispatch({
type: 'HIDE_NAVIGATION',
});
}
else{
NavigationStore.dispatch({
type: 'SHOW_NAVIGATION',
});
}
}
};
I was using a pretty strange approach, I did not use the react-redux package for the whole thing and couldn't connect my store. I deep dived into https://github.com/bartonhammond/snowflake and got it solved, the snowflake example was really helpful to understand the basics!