Prerequisites
- 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
What You Are Going to Do
- A user logs in.
- As soon as the user enters a name, the app retrieves the last 50 notes, if any.
- The user types something on the stickie pad, and hits the return key to submit.
- 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.
Installing Packages
npm init
to set up your package.json file, and then install these modules.$ npm install webpack --save-dev
$ npm install react react-dom react-addons-css-transition-group --save
$ sudo npm install babel-loader babel-core babel-preset-es2015 babel-preset-react --save
$ npm install pubnub --save
Configure the App Structure and Web Server
01
02
03
04
05
06
07
08
09
10
11
| ├── /app │ ├── app.jsx │ ├── stickie.jsx │ ├── stickieList.jsx ├── /dist ├── /css ├── /images ├── /node_modules ├── index.html ├── package.json └── webpack.config.js |
1
2
3
4
5
6
7
| var webpack = require( 'webpack' ); module.exports = { entry: './app/app.jsx' , output: {path: './dist' , filename: 'bundle.js' }, watch: true , module: {...} } |
watch: true
, you ensure that webpack will watch your file changes and rebuild your output file automatically.Creating the index.html File
01
02
03
04
05
06
07
08
09
10
11
12
| <! 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 > |
id=”container”
in the body. This is where your React app will be inserted.Running Webpack Dev Server
$ ./node_modules/.bin/webpack-dev-server
1
2
3
| "scripts" : { "start" : "webpack-dev-server" }, |
npm start
command instead.Create React Components With ES6
1
2
3
4
| import React from 'react' ; import ReactDOM from 'react-dom' ; import StickieList from './stickieList' ; import 'pubnub' ; |
CollabStickies
, which extends the React.Component
class, using this ES6 class declaration. This is equivalent to the React.createClass
method with ES5:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
| class CollabStickies extends React.Component { constructor(props) { super (props); this .state = { stickieList: [] } } componentWillMount() { … // will explain later } ... render() { return (
|
);
}
}
stickieList
array. We will update the array each time we get a new stickie note, using this.setState()
.StickieWritable
andStickieList
are included. You can pass the mutable props and states to the components to use. We are going to define them later.Rendering the DOM Node With Data Binding
ReactDOM.render()
, which comes with the react-dom
package, render theCollabStickies
component on the DOM node in your HTML.
1
2
3
4
| ReactDOM.render(
document.getElementById( 'container' ) ); |
props
. This data is used for theCollabStickies
component and is passed to its child components.window.prompt()
to get a username, and then give a random color of stickie notes when the app is loaded.
1
2
3
4
| var username = window.prompt( 'Your name' ); const colors = [ 'yellow' , 'pink' , 'green' , 'blue' , 'purple' ]; var color = colors[~~(Math.random() * colors.length)]; |
Using PubNub for Collaboration
Initializing PubNub
01
02
03
04
05
06
07
08
09
10
11
| 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' ; |
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!)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
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.subscribe()
within componentWillMount()
, which is invoked immediately before the initial rendering occurs in the app lifecycle.
1
2
3
4
5
6
7
8
| componentWillMount() { pubnub.subscribe({ channel: channel, restore: true , connect: () => this .connect(), message: (m) => this .success(m) }); } |
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.setState
automatically updates the view.
1
2
3
4
| success(m) { let newList = [m].concat( this .state.stickieList); this .setState({stickieList: newList}); } |
=>
. This is called arrow functions, which has a shorter syntax than the ES5 function expressions. Also, this expression lexically binds thethis
value. Again, with Babel, we can leverage all the ES6 awesomeness!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.
01
02
03
04
05
06
07
08
09
10
11
12
13
| 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}); } } }); } |
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
StickieWritable
. It is a stickie note component that takes a user input.
1
2
3
4
5
6
7
| render() { return (
); } |
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:
01
02
03
04
05
06
07
08
09
10
11
12
| 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:
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 theStickieWritable
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.
01
02
03
04
05
06
07
08
09
10
11
12
13
| import React from 'react' ; import ReactDOM from 'react-dom' ; export default class Stickie extends React.Component { render() { return (
); } } |
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.
1
2
3
4
5
| 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:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
| export default class StickieList extends React.Component { render() { let items = ( this .props.stickieList || []).map((item) =>
); return (
{items} ) } } |
The defined components can be animated using
. Set the transitionName
, which you need to use in your CSS to define the animation style. Also, notice the key attribute in
. You need to use a unique key for each list to animate each component when you are using
.
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:
01
02
03
04
05
06
07
08
09
10
| .animation-enter { opacity: 0.1 ; transform: scale( 1.3 ); transition: all 1 s 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!
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 atapp.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!
Comments
Post a Comment