React is a great library used for building user interfaces. What if we want to integrate it with another library that is responsible for receiving data? In the previous chapter, we outlined five tasks that our Snapterest web application should be able to perform. We decided that four of them were related to the user interface, but one of them was all about receiving data; receive tweets from the Snapkite Engine server in real time.
In this chapter, we'll learn how to integrate React with the external JavaScript library and what React component lifecycle methods are, all while solving the very important task of receiving data.
As we discussed earlier in this book, our Snapterest web application will consume a live stream of tweets. In Chapter 1, Installing Powerful Tools for Your Project, you installed the Snapkite Engine library that connects to the Twitter Streaming API, filters the incoming tweets, and sends them to our client application. In turn, our client application needs a way of connecting to that live stream and listening for the new tweets.
Luckily, we don't need to implement this functionality ourselves because we can reuse another Snapkite module called snapkite-stream-client
. Let's install this module.
Navigate to the ~/snapterest
directory and run the following command:
npm install --save snapkite-stream-client
It will install the snapkite-stream-client
module, and add it to package.json
as a dependency.
Now we're ready to reuse the snapkite-stream-client
module in one of our React components.
In the previous chapter, we created the Application
component with two child components: Stream
and Collection
. In this chapter, we'll create our Stream
component.
Let's start by creating the ~/snapterest/source/components/Stream.react.js
file:
var React = require('react'); var SnapkiteStreamClient = require('snapkite-stream-client'); var StreamTweet = require('./StreamTweet.react'); var Header = require('./Header.react'); var Stream = React.createClass({ getInitialState: function () { return { tweet: null } }, componentDidMount: function () { SnapkiteStreamClient.initializeStream(this.handleNewTweet); }, componentWillUnmount: function () { SnapkiteStreamClient.destroyStream(); }, handleNewTweet: function (tweet) { this.setState({ tweet: tweet }); }, render: function () { var tweet = this.state.tweet; if (tweet) { return ( <StreamTweet tweet={tweet} onAddTweetToCollection={this.props.onAddTweetToCollection} /> ); } return ( <Header text="Waiting for public photos from Twitter..." /> ); } }); module.exports = Stream;
First, we will import the following modules that our Stream
component depends on:
React
: This is a React libraryStreamTweet
and Header
: These are React componentssnapkite-stream-client
: This is a utility libraryThen, we will define our React component. Let's take a look at the methods that our Stream
component implements:
getInitialState()
componentDidMount()
componentWillUnmount()
handleNewTweet()
render()
We're already familiar with the getInitialState()
and render()
methods; they are part of React's API. You already know that any React component must implement at least the render()
method. Let's take a look at the render()
method of our Stream
component:
render: function () { var tweet = this.state.tweet; if (tweet) { return ( <StreamTweet tweet={tweet} onAddTweetToCollection={this.props.onAddTweetToCollection} /> ); } return ( <Header text="Waiting for public photos from Twitter..." /> ); }
As you can see, we created a new tweet
variable that references the tweet
property, which is part of a component's state object. We then check whether that variable has a reference to an actual tweet
object, and if it does, our render()
method returns the StreamTweet
component, or else, it returns the Header
component.
The StreamTweet
component renders a header and the latest tweet from a stream, whereas the Header
component renders only a header.
Have you noticed that our Stream
component doesn't render anything itself, but rather returns one of the two other components that do the actual rendering? The purpose of a Stream
component is to encapsulate our application's logic and delegate rendering to the other React components. In React, you should have at least one component that encapsulates your application's logic, stores, and manages your application's state. This is usually a root component or one of the high-level components in your component hierarchy. All the other child React components should have no state if possible. If you think of all the React components as Views
, then our Stream
component is a ControllerView
.
Now that we know what a Stream
component renders, let's discuss it in the other methods:
getInitialState: function () { return { tweet: null } },
The getInitialState()
method returns the initial state object with a tweet
property, which is set to null
. Our Stream
component will receive an endless stream of new tweets, and it needs to re-render its child components every time a new tweet is received. In order to achieve this, we need to store the current tweet in the component's state. Once we update its state, React will call its render()
method and re-render all of its child components. For this purpose, we will implement the handleNewTweet()
method:
handleNewTweet: function (tweet) { this.setState({ tweet: tweet }); },
The handleNewTweet()
method takes a tweet
object, and sets it as a new value for the component state's tweet
property.
Where does that new tweet come from and when does it come? Let's take a look at our componentDidMount()
method:
componentDidMount: function () { SnapkiteStreamClient.initializeStream(this.handleNewTweet); },
This function calls the initializeStream()
method of the SnapkiteStreamClient
object, and passes a this.handleNewTweet
callback function as its argument. SnapkiteStreamClient
is an external library with an API that we're using to initialize a stream of tweets. The this.handleNewTweet
function will be called for every tweet that SnapkiteStreamClient
receives.
Why did we name this method componentDidMount()
? We didn't. React did. In fact, the componentDidMount()
method is part of React's API. It's one of the React component's lifecycle methods. It's called only once, immediately after React has finished the initial rendering of our component. At this point, React has created a DOM tree, which is represented by our component, and now we can access that DOM with another JavaScript library.
componentDidMount()
is a perfect place for integrating React with another JavaScript library. This is where we connect to a stream of tweets using the external SnapkiteStreamClient
library.
Now we know when to initialize the external JavaScript libraries in our React components, but what about the reverse process—when should we uninitialize and clean up everything that we've done in the componentDidMount()
method? It's a good idea to clean up everything before we unmount our components. For this purpose, React API offers us another component lifecycle method—componentWillUnmount()
:
componentWillUnmount: function () { SnapkiteStreamClient.destroyStream(); },
The componentWillUnmount()
method is called by React just before React unmounts the component. As you can see in the componentWillUnmount()
method, you're calling the destroyStream()
method of the SnapkiteStreamClient
object. destroyStream()
cleans up our connection to SnapkiteStreamClient
, and we can safely unmount our Stream
component.
You might be wondering what are the component lifecycle methods and why do we need them?
18.222.225.231