How to build a news app with JavaScript and React Native

May 09, 2018 0 Comments

How to build a news app with JavaScript and React Native

 

 

Requirements for building the app:

  • A basic understanding of the JavaScript language.
  • Node.js, and react native.
  • Libraries used: moment, react-native, react-native-elements.

If you’re not familiar with these resources, don’t worry — they are quite easy to use.

The topics we will cover in the post are:

  • News API
  • Fetch API
  • FlatList
  • Pull down to refresh
  • Linking

And more…so let’s get started!
You can find the full project repo HERE.

News API

A simple and easy-to-use API that returns JSON metadata for headlines and articles live all over the web right now. — NewsAPI.org

First, you should go ahead and sign up for News Api to get your free apiKey (your authentication key).

Create a new React Native project, and call it newsapp (or whatever you want). In the project directory, make a new folder and call it src . In src create a folder an name it components . So your project directory should look something like this:

screenshot

In the src folder, create a new file called news.js . In this file we are going to fetch the JSON that contains the headlines from the News API.

news.js

const url = "https://newsapi.org/v2/top-headlines?country=us&apiKey=YOURAPIKEYHERE"; export async function getNews() { let result = await fetch(url).then(response => response.json()); return result.articles; 
}

Make sure you replace YOURAPIKEY_HERE with your own API key. For more information about the News API, go to newsapi docs.

Now we declare the getNews function, which is going to fetch the articles for us. Export the function so we can use it in our App.js file.

App.js

import React from 'react'; 
import { FlatList } from 'react-native'; // Import getNews function from news.js
import { getNews } from './src/news';
// We'll get to this one later
import Article from './src/components/Article'; export default class App extends React.Component { constructor(props) { super(props); this.state = { articles: [], refreshing: true }; this.fetchNews = this.fetchNews.bind(this); } // Called after a component is mounted componentDidMount() { this.fetchNews(); } fetchNews() { getNews() .then(articles => this.setState({ articles, refreshing: false })) .catch(() => this.setState({ refreshing: false })); } handleRefresh() { this.setState( { refreshing: true }, () => this.fetchNews() ); } render() { return ( <FlatList data={this.state.articles} renderItem={({ item }) => <Article article={item} />} keyExtractor={item => item.url} refreshing={this.state.refreshing} onRefresh={this.handleRefresh.bind(this)} /> ); }
}

In the constructor, we define the initial state. articles will store our articles after we fetch them, and refreshing will help us in refresh animation. Notice that I setrefreshing to true, because when we start the app, we want the animation to start while we load the articles.

componentDidMount is invoked immediately after a component is mounted. Inside it we call the fetchNews method.

componentDidMount() { this.fetchNews(); 
}

In fetchNews we call getNews() which returns a promise. So we use the .then() method which takes a callback function, and the callback function takes an argument (the articles).

Now assign the articles in the state to the articles argument. I only typed articles because it’s a new ES6 syntax that means { articles: articles } , and we set refreshing to false to stop the spinner animation.

fetchNews() { getNews().then( articles => this.setState({ articles, refreshing: false }) ).catch(() => this.setState({ refreshing: false })); 
}

.catch() is called in rejected cases.

handleRefresh starts the spinner animation and call fetchNews(). We pass () => this.fetchNews() , so it’s called immediately after we assign the state.

handleRefresh() { this.setState({ refreshing: true },() => this.fetchNews()); 
}

In the render method, we return a FlatList element. Then we pass some props. data is the array of articles from this.state. The renderItem takes a function to render each item in the array, but in our case it just returns the Article component we imported earlier (we’ll get there). And we pass the article item as a prop to use later in that component.

Article.js

In src/components create a new JavaScript file and call it Article.js.

Let’s start by installing two simple libraries using npm: react-native-elements, which gives us some premade components we could use, and moment that will handle our time.

run using terminal/cmd:

npm install --save react-native-elements moment

In Article.js:

import React from 'react'; 
import { View, Linking, TouchableNativeFeedback } from 'react-native';
import { Text, Button, Card, Divider } from 'react-native-elements';
import moment from 'moment'; export default class Article extends React.Component { render() { const { title, description, publishedAt, source, urlToImage, url } = this.props.article; const { noteStyle, featuredTitleStyle } = styles; const time = moment(publishedAt || moment.now()).fromNow(); const defaultImg = 'https://wallpaper.wiki/wp-content/uploads/2017/04/wallpaper.wiki-Images-HD-Diamond-Pattern-PIC-WPB009691.jpg'; return ( <TouchableNativeFeedback useForeground onPress={() => Linking.openURL(url)} > <Card featuredTitle={title} featuredTitleStyle={featuredTitleStyle} image={{ uri: urlToImage || defaultImg }} > <Text style={{ marginBottom: 10 }}> {description || 'Read More..'} </Text> <Divider style={{ backgroundColor: '#dfe6e9' }} /> <View style={{ flexDirection: 'row', justifyContent: 'space-between' }} > <Text style={noteStyle}>{source.name.toUpperCase()}</Text> <Text style={noteStyle}>{time}</Text> </View> </Card> </TouchableNativeFeedback> ); }
} const styles = { noteStyle: { margin: 5, fontStyle: 'italic', color: '#b2bec3', fontSize: 10 }, featuredTitleStyle: { marginHorizontal: 5, textShadowColor: '#00000f', textShadowOffset: { width: 3, height: 3 }, textShadowRadius: 3 }
};

There is a lot going on here. First, we start by destructuring the article prop and the styles object defined below the class.

In render we define time to store the time for when the article was published. We use the moment library to convert the date to the time passed since then, and we pass publishedAt or time from now if publishedAt is null.

defaultImg is assigned an image URL in case the URL of the article image is null.

The render method returns TouchableNativeFeedback (use TouchableOpacity instead if it does not work on your platform) to handle when the user presses the card. We pass it some props: useForground which tells the element to use the foreground when displaying the ripple effect on the card, and onPress , which takes a function and executes it when the user presses the card. We passed () => Linking.openUrl(url) which simply opens the URL to the full article when we press the card.

The card takes three props: featuredTitle which is just a fancy title placed over the image you could use title instead if you want, featuredTitleStyle to style it, and image which is the article image from the article prop. Otherwise, if its null , it’s going to be the defaultImg.

.. featuredTitle={title} featuredTitleStyle={featuredTitleStyle} image={{ uri: urlToImage || defaultImg }} 
..

As for the text element, it will hold the description for the article.

<Text style={{ marginBottom: 10 }}>{description}</Text>

We added a divider to separate the description from time and source name.

<Divider style={{ backgroundColor: '#dfe6e9' }} />

Below the Divider , we have a View that contains the source name and the time the article was published.

.. 
<View style={{ flexDirection: row, justifyContent: space-between }} > <Text style={noteStyle}>{source.name.toUpperCase()}</Text> <Text style={noteStyle}>{time}</Text>
</View>
..

After the class, we defined the styles for these components.

Now if we run the app:

screenshot

and we can refresh the app

screenshot

There you go! The source code for the app is available on GitHub HERE you can improve upon it and make a pull request😄.

I hope you enjoyed my article! If you have any questions at all, feel free to comment or reach me on twitter and I will definitely help :)

Also don't forget to share the article😄👇.


Tag cloud