神刀安全网

Building a Collaborative Web App With PubNub, React.js, and ES6

In my previous tutorials, I demonstrated how to prototype Internet of Things devices , and also  created data visualizations from hardware sensors , using  PubNub ’s Data Stream Network. In this tutorial, I am going to show you how to use PubNub to build a real-time collaborative web app using  React.js , which lets you manipulate the DOM very efficiently, and the next generation of JavaScript,  ES6 .

Building a Collaborative Web App With PubNub, React.js, and ES6

Live Demo: Collaborative Stickies

I have created two versions of the same Stickie Note app: the one I hosted on this CodePen uses CDN-hosted versions of React, and another is on GitHub , using package managers. In this tutorial, I use the “lite” version of the latter. I will walk through how to build the app, using all the goodies: npm, webpack, Babel for JSX, and ES6!

Prerequisites

To follow along, you’ll need:

  • basic understanding of React
  • working knowledge of the npm package manager to download, install, and manage dependencies
  • working knowledge of the webpack module builder, to bundle JavaScript and other assets for the browser (it works similar to grunt or gulp)
  • Node.js and npm installed on your machine

This tutorial doesn’t cover how to get started with React. However, you can learn more from many other excellent Envato Tuts+ tutorials .

What You Are Going to Do

You are going to build a simple web app using PubNub now. PubNub is a Data Stream Network (DSN) that provides a global infrastructure which allows you to build and scale real-time apps and IoT devices easily. Here, you will create shareable “stickie notes”. This is the app’s user-flow:

  1. A user logs in.
  2. As soon as the user enters a name, the app retrieves the last 50 notes, if any.
  3. The user types something on the stickie pad, and hits the return key to submit.
  4. The new stickie note appears along with the other notes on your browser, as well as every other browser of all users who are currently online.

Now, let’s get started!

Installing Packages

In your app’s directory, run npm init to set up your package.json file, and then install these modules.

Install webpack module builder, which compiles, concatenates, minifies, and compresses static assets for the front-end:

$ npm install webpack –save-dev

Install webpack web server to run a local server:

$ npm install webpack-dev-server --save-dev

Install React, React DOM, and CSS animation add-ons:

$ npm install react react-dom react-addons-css-transition-group --save

Install Babel to use JSX and ES6. We’re going to write with ES6 (ES 2015), which is next-generation JavaScript, with the help of Babel, a compiler:

$ sudo npm install babel-loader babel-core babel-preset-es2015 babel-preset-react --save

Install PubNub for real-time communication:

$ npm install pubnub --save

Configure the App Structure and Web Server

Create your app structure similar to this:

├── /app │   ├── app.jsx │   ├── stickie.jsx │   ├── stickieList.jsx ├── /dist ├── /css ├── /images ├── /node_modules ├── index.html ├── package.json └── webpack.config.js

And configure webpack.config.js :

var webpack = require('webpack'); module.exports = {   entry: './app/app.jsx',   output: {path: './dist', filename: 'bundle.js'},   watch: true,   module: {...} }

See the entire config file on this GitHub repo .

Basically, you are setting an entry file (top-level file) and the output destination where all your js (and .jsx) files will be built into a single file, after you run the webpack command. Also, by setting watch: true , you ensure that webpack will watch your file changes and rebuild your output file automatically.

Creating the index.html File

Include the script bundle.js in your index.html file:

<!DOCTYPE html> <html>   <head>     <meta charset="utf-8">     <title>Collaborative Stickies</title>     <link rel="stylesheet" href="css/style.css" />   </head>   <body>     <section id="container"></section>     <script src="dist/bundle.js"></script>   </body> </html>

Also, notice the element with an id=”container” in the body. This is where your React app will be inserted.

Running Webpack Dev Server

You can run your dev server with the command,

$ ./node_modules/.bin/webpack-dev-server

Or you can set it up in your package.json by adding this line:

"scripts": {   "start": "webpack-dev-server" },

So that you can run the server with the npm start command instead.

In your browser, go to http://localhost:8080/webpack-dev-server/ , and you should see your application (a blank HTML page thus far) running there.

Create React Components With ES6

Open up a new app.jsx file under the app directory, as you configured for an entry point in your webpack.config.js . As you can see from the file extension, we are going to use the JSX JavaScript syntax extension.

First, import the modules and files that are required for app.jsx

import React from 'react'; import ReactDOM from 'react-dom'; import StickieList from './stickieList'; import 'pubnub';

The import statement, newly introduced in ES6, is used to import functions, objects or primitives that have been exported from an external module or script.

Then define a class, CollabStickies , which extends the React.Component class, using this ES6 class declaration. This is equivalent to the  React.createClass method with ES5:

class CollabStickies extends React.Component {   constructor(props) {     super(props);     this.state = {       stickieList: []     }   }    componentWillMount() {     … // will explain later   } ...   render() {     return (       <div>         <StickieWritable username={this.props.username} color={this.props.color} />         <StickieList stickieList={this.state.stickieList} />       </div>     );   } }

In the constructor function, you are setting the initial state of this mutable data, the stickieList array. We will update the array each time we get a new stickie note, using this.setState() .

In the render function, use JSX to define HTML template-like virtual DOM elements. In this case, the custom components StickieWritable and StickieList are included. You can pass the mutable props and states to the components to use. We are going to define them later.

When you build the app, Babel will transpile all this ES6 and JSX syntax into ES5 that browsers can render just fine.

Rendering the DOM Node With Data Binding

With ReactDOM.render() , which comes with the  react-dom package, render the CollabStickies component on the DOM node in your HTML.

ReactDOM.render(   <CollabStickies username={username} color={color} />,   document.getElementById('container') );

Here, you notice the username and color props . This data is used for the CollabStickies component and is passed to its child components.

The values should be obtained from the user login; however, to simplify the app for this exercise, let’s just use a simple window.prompt() to get a username, and then give a random color of stickie notes when the app is loaded. 

var username = window.prompt('Your name');  const colors = ['yellow', 'pink', 'green', 'blue', 'purple']; var color = colors[~~(Math.random() * colors.length)];
Building a Collaborative Web App With PubNub, React.js, and ES6

Although I am using the browser-native prompt dialog here, in reality, I recommend you create another UI component with login functions, or use the third-party dialog box component. There are many reusable components you can find, such as Elemental UI’s Modal , and Material UI’s Dialog .

Using PubNub for Collaboration

Now, you are going to use PubNub to make the app collaborative.

PubNub is a globally distributed data stream network that allows you to build real-time applications easily. Its core feature, pub/sub, sends and receives data between multiple users simultaneously.

In this app, anybody who “logged in” can post messages on stickie notes and share them with other users.

Building a Collaborative Web App With PubNub, React.js, and ES6

To use PubNub in your app, make sure that the pubnub module has been installed and imported in the top of your file.

Initializing PubNub

First, you need to initialize it to create an instance of the Pubnub object. You need your API keys during the instantiation, so please sign up to PubNub to get your own keys.

const publish_key =  'pub-c-1d17120...'; // your pub key const subscribe_key  = 'sub-c-85bdc...'; // your sub key  const pubnub = require('pubnub').init({                            publish_key   : publish_key,   subscribe_key : subscribe_key,   ssl: true,   uuid: username });  const channel = 'stickie-notes';

Here, you are assigning the username obtained from the “login” process as a uuid , unique identifier. (In this exercise, we take any string entered by a user as an uuid, but in reality, you need a real login system so that each uuid is actually unique, with no duplications!)

Also, notice I am using the ES6 const declaration, instead of var for these global constant values. In ES6 a const acts as a read-only variable and represents a constant reference to a value. In the later example, you will also see the newly introduced let , which is a block scope local variable.

Subscribing to Messages

To create the sharable notes app, you are going to use PubNub’s publish() method to send your note to everybody, while subscribe() lets other users receive all the notes. The subscribe() method is called automatically every time somebody publishes a new note.

In your React app, let’s call the subscribe() within componentWillMount() , which is invoked immediately before the initial rendering occurs in the app lifecycle.

componentWillMount() {   pubnub.subscribe({     channel: channel,     restore: true,     connect: () => this.connect(),     message: (m) => this.success(m)   }); }

The subscribe method is asynchronous, and when each operation is successfully completed, the message callback is called. At the callback, let’s update the stickie note list by setting the state of the stickieList array, which was defined in the constructor at the beginning.

In React, modifying your data with setState automatically updates the view.

success(m) {    let newList = [m].concat(this.state.stickieList);   this.setState({stickieList: newList}); }

We are creating the view (a UI component) later.

In the subscribe callbacks, you probably have noticed the funny-looking syntax with arrows, => . This is called arrow functions, which has a shorter syntax than the ES5 function expressions. Also, this expression lexically binds the this value. Again, with Babel, we can leverage all the ES6 awesomeness!

Also, we are using the optional connect callback to the subscribe method to retrieve “history”. This will fetch past data when the connection to PubNub is established for the first time.

connect() {    pubnub.history({     channel: channel,     count: 50,     callback: (m) => {       m[0].reverse();       for (var v of m[0]) {         let newList = this.state.stickieList.concat(v);         this.setState({stickieList: newList});       }     }   }); }

The history() is a part of PubNub’s Storage and Playback feature, and in this case, it fetches the last 50 messages from PubNub. At the success callback, update the view by setting the state of the stickieList array here too.

Publishing Messages

Let’s create a class, StickieWritable . It is a stickie note component that takes a user input.

It renders like this:

render() {   return (     <div className={'stickie-note writable ' + this.props.color}>       <textarea type='text' placeholder='Your new note...' onKeyUp={this.handleTextChange.bind(this)} />     </div>     ); }

In the textarea , listen to the onKeyUp event, and each time the event is triggered, call the  handleTextChange function to check if the key was a return/enter key. Notice that I am binding this when calling the function. Unlike the React.createClass() , which is React’s ES5 method to create a class, the ES6 class does not autobind methods to the instance of an object, so you need to bind it by yourself. (There are several different ways to achieve the same thing.)

In the handleTextChange function, publish the text and user data to PubNub:

var data = {   username: this.props.username,   color: this.props.color,   text: e.target.value,   timestamp: Date.now() };  pubnub.publish({   channel: channel,    message: data,    callback: e.target.value = '' // resetting the text field });

Now, when a user types some text in a notepad and presses return, the message will be sent to PubNub, and all other users receive the message simultaneously (within ¼ sec!).

Creating UI Components

The app UI consists of a few UI components, which look like this:

Building a Collaborative Web App With PubNub, React.js, and ES6

1. CollabStickies

2. StickieWritable

3. Stickie

4. StickieList

Components 1 and 2 have been already taken care of, so let’s create component 3, an individual stickie note component.

Create a new file stickie.jsx to render the UI using JSX. Unlike the StickieWritable component, this is a read-only UI component with no UX functionality. It only has a render() function to draw a stickie note with text using prop data.

Basically, every time the user receives a new message from another user, the message is rendered in a new stickie component.

import React from 'react'; import ReactDOM from 'react-dom';  export default class Stickie extends React.Component {   render() {     return (       <div className={'stickie-note ' + this.props.color} >         <p className='note'>{this.props.text}</p>         <p className='username'>{this.props.username}</p>       </div>       );   } }

Next, we are going to create another UI component, stickieList.jsx , which is a container for this component and contains a bunch of stickie notes together.

Animating Components

Import Stickie.jsx and all other dependencies into StickieList.jsx . Here, I am using a ReactCSSTransitionGroup addon and a custom web font.

import React from 'react'; import ReactDOM from 'react-dom'; import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup'; import Stickie from './stickie'; import webfontloader from 'webfontloader'

You can install the Web font loader with npm:

$ npm install webfontloader

Then you can load any custom fonts of your choice. You can take a look at the source code to see how a custom Google font is imported.

In render() , use an ES6 arrow function and map() to iterate the array, and use  stickieList to render each Stickie component you just created:

export default class StickieList extends React.Component {   render() {     let items = (this.props.stickieList || []).map((item) =>        <li key={item.username + '-' + item.timestamp} >         <div className="stickieWrapper">           <Stickie text={item.text} color={item.color} username={item.username}/>         </div>       </li>);      return (       <ReactCSSTransitionGroup transitionName='animation' transitionEnterTimeout={500} transitionLeaveTimeout={500} component='ul' id="stickiesList">         {items}       </ReactCSSTransitionGroup>       )   } }

The defined components can be animated using <ReactCSSTransitionGroup> . Set the transitionName , which you need to use in your CSS to define the animation style. Also, notice the key attribute in <li> . You need to use a unique key for each list to animate each component when you are using <ReactCSSTransitionGroup> .

React adds extra class names. For example, when your transitionName is ‘ animation ’, you will also have ‘ animation-enter ’, ‘ animation-enter-active ’, ‘ animation-leave ’, and ‘ animation-leave-active ’.

Here’s the code in /css/style.css:

.animation-enter {   opacity: 0.1;   transform: scale(1.3);   transition: all 1s ease-out;  } .animation-enter.animation-enter-active {   opacity: 1;   transform: scale(1);  } ...

Now, you have just built a real-time collaborative app with React and PubNub! I hope you enjoyed the tutorial!

Building a Collaborative Web App With PubNub, React.js, and ES6

You can view the entire code, including CSS, in this GitHub repo . Although, in this tutorial, I was using the “lite” version, app-lite.jsx , you can take a look at app.jsx for more features.

If you are interested in building more real-time applications, such as chat apps, multiplayer games, trading apps, etc., go to PubNub and find more resources!

References

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Building a Collaborative Web App With PubNub, React.js, and ES6

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址