Thursday, August 11, 2016

How to Create a News Reader With React Native: Web Page Component_part 1


In the first part , you learned how to set up React Native on your machine, create and use custom components, and use third party libraries, such as moment.js. In this tutorial, you learn how to make network requests using fetch, render a web page using the built-in WebView component, and run the app on a physical device.

1. Fetch API Wrapper

In the first part of this series, we used the api function, but we haven't defined it yet. Start by creating a src directory and add a file to it, api.js. Open the file and add the following to it:
  1. module.exports = function(url){
  2.      
  3.     return fetch(url).then(function(response){
  4.         return response.json();
  5.     }).then(function(json){
  6.         return json;
  7.     });
  8. }
This file uses the fetch function, which is by default available in React Native. This function allows the app to perform network requests. If you've used jQuery, it's pretty similar to the $.ajax function. You specify a URL and some optional data, and you get a response back.

The only difference is that you need to do a bit of extra work. The function for capturing the first promise returns the raw response, which means that you have to call the json method on the response to get the promise that returns the JSON string. So you have to return the result from this and capture the promise by calling the then function once again and pass in the function that will be called once the promise resolves.

The JSON string would then be passed as an argument to this function so we just return it. The fetch method returns a promise so when we call the api method, we still have to call the then method to capture the actual response, just like we did in the first part of this series.
  1. api(story_url).then(
  2.     (story) => {
  3.        ...
  4.     }
  5. );
2. WebPageComponent
  The WebPage component is responsible for rendering a web page. It uses the WebView component to do so.
  1. var React = require('react-native');
  2. var {
  3.   AppRegistry,
  4.   StyleSheet,
  5.   Text,
  6.   View,
  7.   WebView
  8. } = React; 
  9. var Button = require('react-native-button');
  10. var GiftedSpinner = require('react-native-gifted-spinner'); 
  11. var _ = require('lodash');
  12.  var WebPage = React.createClass({
  13.     getInitialState: function() {
  14.         return {
  15.             isLoading: true
  16.         };
  17.     }, 
  18.     render: function(){             
  19.         return (<View style={styles.container}>         
  20.             <View style={styles.webview_header}>
  21.               <View style={styles.header_item}>
  22.                 <Button style={styles.button} onPress={this.back}>Back</Button>
  23.               </View>
  24.               <View style={styles.header_item}>
  25.                 <Text style={styles.page_title}>{this.truncate(this.state.pageTitle)}</Text>
  26.               </View>
  27.               <View style={[styles.header_item, styles.spinner]}>
  28.                 { this.state.isLoading && <GiftedSpinner /> }
  29.               </View>
  30.             </View>
  31.             <View style={styles.webview_body}>
  32.                 <WebView 
  33.                     url={this.props.url}
  34.                     onNavigationStateChange={this.onNavigationStateChange}                     
  35.                 />
  36.             </View>
  37.         </View>); 
  38.     }, 
  39.     truncate: function(str){
  40.         return _.truncate(str, 20);
  41.     }, 
  42.     onNavigationStateChange: function(navState) {         
  43.         if(!navState.loading){
  44.             this.setState({
  45.                 isLoading: false,
  46.                 pageTitle: navState.title
  47.             });
  48.         }
  49.     },     
  50.     back: function(){
  51.        this.props.navigator.pop();
  52.     }
  53. });  
  54. var styles = StyleSheet.create({
  55.     container: {
  56.         flex: 1
  57.     },
  58.     webview_header: {
  59.         paddingLeft: 10,
  60.         backgroundColor: '#FF6600',
  61.         flex: 1,
  62.         justifyContent: 'space-between',
  63.         flexDirection: 'row'
  64.     },
  65.     header_item: {
  66.         paddingLeft: 10,
  67.         paddingRight: 10,
  68.         justifyContent: 'center'
  69.     },
  70.     webview_body: {
  71.         flex: 9
  72.     },
  73.     button: {
  74.         textAlign: 'left',
  75.         color: '#FFF'
  76.     },
  77.     page_title: {
  78.         color: '#FFF'
  79.     },
  80.     spinner: { 
  81.         alignItems: 'flex-end'
  82.     }
  83. }); 
  84. module.exports = WebPage;
First, we do some housekeeping by creating the variables we need and requiring the libraries we'll be using.
  1. var React = require('react-native');
  2. var {
  3.   AppRegistry,
  4.   StyleSheet,
  5.   Text,
  6.   View,
  7.   WebView
  8. } = React; 
  9. var Button = require('react-native-button');
  10. var GiftedSpinner = require('react-native-gifted-spinner');
  11. var _ = require('lodash');
Next, we create the WebPage component.
  1. var WebPage = React.createClass({
  2.     ...
  3. });
We set isLoading to true as the default state. This property is responsible for determining whether or not to show the spinner. By default, the spinner should be visible to indicate that the page is loading.
  1. getInitialState: function() {
  2.     return {
  3.         isLoading: true
  4.     };
  5. },
Next, we render the component. Like the news item component, this one also has a header and a body. The header contains a back button, the title of the page, and a spinner.
  1. render: function(){         
  2.     return (<View style={styles.container}>     
  3.         <View style={styles.webview_header}>
  4.           <View style={styles.header_item}>
  5.             <Button style={styles.button} onPress={this.back}>Back</Button>
  6.           </View>
  7.           <View style={styles.header_item}>
  8.             <Text style={styles.page_title}>{this.truncate(this.state.pageTitle)}</Text>
  9.           </View>
  10.           <View style={[styles.header_item, styles.spinner]}>
  11.             { this.state.isLoading && <GiftedSpinner /> }
  12.           </View>
  13.         </View> 
  14.         <View style={styles.webview_body}>
  15.             <WebView 
  16.                 url={this.props.url}
  17.                 onNavigationStateChange={this.onNavigationStateChange}
  18.             />
  19.         </View>
  20.     </View>);
  21. },
The body contains the WebView component. The WebView component has a url and onNavigationStateChange attributes. The url is the URL that was passed from the viewPage function in the NewsItems component earlier. So when the following code is executed:
  1. this.props.navigator.push({name: 'web_page', url: url});
The renderScene method in index.android.js also gets executed and the URL is passed to it:
  1. renderScene: function(route, navigator) {
  2.  
  3.     var Component = ROUTES[route.name];
  4.     return (
  5.         <Component route={route} navigator={navigator} url={route.url} />
  6.     );
  7. },
That is how we have access to the URL by extracting it from the props: this.props.url.

Let's go back to the attributes added to the WebView component. We have the onNavigationStateChange attribute, which is used for specifying the function to execute whenever the web view navigates to a new page. This is what that function looks like:
  1. onNavigationStateChange: function(navState) {
  2.      
  3.     if(!navState.loading){
  4.         this.setState({
  5.             isLoading: false,
  6.             pageTitle: navState.title
  7.         });
  8.     }
  9. },
When the above function is called, the navState is passed along as an argument. This contains information about the current state of the web view, such as the title of the page and whether or not it is currently loading. This is the perfect place to update the state. When the page is no longer loading, we set isLoading to false and set a value for the pageTitle.

Next, we have the back function, which makes the navigator go back one page. This gets called whenever the user taps the back button in the header.
  1. back: function(){
  2.    this.props.navigator.pop();
  3. }
The truncate function limits the length of whatever is passed into the function. We use this function to limit the text for the page title of the web page.
  1. truncate: function(str){
  2.     return _.truncate(str, 20);
  3. },
The stylesheet looks like this:
  1. var styles = StyleSheet.create({
  2.     container: {
  3.         flex: 1
  4.     },
  5.     webview_header: {
  6.         paddingLeft: 10,
  7.         backgroundColor: '#FF6600',
  8.         flex: 1,
  9.         justifyContent: 'space-between',
  10.         flexDirection: 'row'
  11.     },
  12.     header_item: {
  13.         paddingLeft: 10,
  14.         paddingRight: 10,
  15.         justifyContent: 'center'
  16.     },
  17.     webview_body: {
  18.         flex: 9
  19.     },
  20.     button: {
  21.         textAlign: 'left',
  22.         color: '#FFF'
  23.     },
  24.     page_title: {
  25.         color: '#FFF'
  26.     },
  27.     spinner: {
  28.         alignItems: 'flex-end'
  29.     }
  30. });
Lastly, expose the component to the outside world:
  1. module.exports = WebPage;
Written by Wernher-Bel Ancheta
If you found this post interesting, follow and support us.
Suggest for you:



No comments:

Post a Comment