Navigation indicators

Earlier in the chapter, you were introduced to the ActivityIndicator component. In this section, you'll learn how it can be used when navigating an application that loads data. For example, the user navigates from page (screen) one to page two. However, page two needs to fetch data from the API to display for the user. So while this network call is happening, it makes more sense to display a progress indicator instead of a screen devoid of useful information.

Doing this is actually kind of tricky because you have to make sure that the data required by the screen is fetched from the API each time the user navigates to the screen. Your goals should be the following:

  • Have the Navigator component automatically fetch API data for the scene that's about to be rendered.
  • Use the promise that's returned by the API call as a means to display the spinner and hide it once the promise has resolved.

Since your components probably don't care about whether or not a spinner is displayed, let's implement this as a generic higher-order component:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { View, ActivityIndicator } from 'react-native';

import styles from './styles';

// Wraps the "Wrapped" component with a stateful component
// that renders an "<ActivityIndicator>" when the "loading"
// state is true.
const loading = Wrapped =>
class LoadingWrapper extends Component {
static propTypes = {
promise: PropTypes.instanceOf(Promise)
};

state = {
loading: true
};

// Adds a callback to the "promise" that was
// passed in. When the promise resolves, we set
// the "loading" state to false.
componentDidMount() {
this.props.promise.then(
() => this.setState({ loading: false }),
() => this.setState({ loading: false })
);
}

// If "loading" is true, render the "<ActivityIndicator>"
// component. Otherwise, render the "<Wrapped>" component.
render() {
return new Map([
[
true,
<View style={styles.container}>
<ActivityIndicator size="large" />
</View>
],
[false, <Wrapped {...this.props} />]
]).get(this.state.loading);
}
};

export default loading;

This loading() function takes a component—the Wrapped argumentand returns a LoadingWrapper component. The returned wrapper accepts a promise property, and when it resolves or rejects, it changes the loading state to false. As you can see in the render() method, the loading state determines whether the spinner is rendered or the Wrapped component.

With the loading() higher-order function in place, let's take a look at the first screen component that you'll use with react-navigation:

import React from 'react';
import { View, Text } from 'react-native';

import styles from './styles';
import loading from './loading';

const First = loading(({ navigation }) => (
<View style={styles.container}>
<Text
style={styles.item}
onPress={() => navigation.navigate('Second')}
>
Second
</Text>
<Text
style={styles.item}
onPress={() => navigation.navigate('Third')}
>
Third
</Text>
</View>
));

export default First;

This module exports a component that's wrapped with the loading() function created earlier. It wraps the First component so that a spinner is displayed while the promise property is pending. The last step is getting that promise into the component whenever the user navigates to a given page. This happens in the route configuration in the App component:

import React from 'react';
import { createStackNavigator } from 'react-navigation';

import First from './First';
import Second from './Second';
import Third from './Third';

export default createStackNavigator(
{
First: {
screen: props => (
<First
promise={new Promise(resolve => setTimeout(resolve, 1000))}
{...props}
/>
)
},
Second: {
screen: props => (
<Second
promise={new Promise(resolve => setTimeout(resolve, 1000))}
{...props}
/>
)
},
Third: {
screen: props => (
<First
promise={new Promise(resolve => setTimeout(resolve, 1000))}
{...props}
/>
)
}
},
{ initialRouteName: 'First' }
);

Instead of just passing the screen components directly to the router config argument of createStackNavigator(), you're passing an object for each screen. The screen property allows you to provide the actual screen component to render. In this case, you're passing it a promise property by calling an API function that resolves data needed by the component. This is how the loading() function is able to display a spinner while waiting for the promise to resolve. The first screen doesn't have to worry about displaying a loading screen.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.127.141