SlideShare a Scribd company logo
1 of 73
Download to read offline
Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 41
Building a React.js-based
QEWD Application
(e) Building out a login sequence
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
What We Want to Do
Copyright © 2016 M/Gateway Developments Ltd
What We Want to Do
Copyright © 2016 M/Gateway Developments Ltd
What We Want to Do
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
Initially
Initial Title Display the form No Content
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
On login error
Initial Title Display the form No Content
Display Error
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
On Successful
login
Hello {{user}}
title
Hide the form Display
Content
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Send username
& password as EWD
message
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Send username
& password as EWD
message
To QEWD
Back-end
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
From
Back-end
Handle response
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Error response
received:
Display Error
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Success response
received:
Re-render
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass down
Username as
prop
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass prop to
Signal successful
login
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass prop to
signal that
content should
be displayed
Copyright © 2016 M/Gateway Developments Ltd
So let's build out the components
In ~/qewd/www/react-demo1
Copyright © 2016 M/Gateway Developments Ltd
MainPage.js
"use strict"
var React = require('react');
var Title = require('./Title');
var Content = require('./Content');
var Login = require('./Login');
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
console.log('Rendering MainPage');
return (
<div>
<Title
controller = {controller}
username = {this.username}
/>
<Login
controller = {controller}
loggedIn = {this.loggedIn}
/>
<Content
controller = {controller}
content = {this.content}
/>
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
MainPage.js
"use strict"
var React = require('react');
var Title = require('./Title');
var Content = require('./Content');
var Login = require('./Login');
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
console.log('Rendering MainPage');
return (
<div>
<Title
controller = {controller}
username = {this.username}
/>
<Login
controller = {controller}
loggedIn = {this.loggedIn}
/>
<Content
controller = {controller}
content = {this.content}
/>
</div>
);
}
});
module.exports = MainPage;
Initial state
variable value
Copyright © 2016 M/Gateway Developments Ltd
MainPage.js
"use strict"
var React = require('react');
var Title = require('./Title');
var Content = require('./Content');
var Login = require('./Login');
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
console.log('Rendering MainPage');
return (
<div>
<Title
controller = {controller}
username = {this.username}
/>
<Login
controller = {controller}
loggedIn = {this.loggedIn}
/>
<Content
controller = {controller}
content = {this.content}
/>
</div>
);
}
});
module.exports = MainPage;
props passed
to child
components
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
Response
from back-end
handled in this
top component
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
Initial values
passed as
props to child
components
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
New values
passed as
props to child
components
if login is
successful
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
State value
changed to
trigger
re-render of
this component
and its child
components
Copyright © 2016 M/Gateway Developments Ltd
Title.js
var React = require('react');
var controller;
var Title = React.createClass({
componentWillMount: function() {
controller = require('./Title-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateHeading(newProps.username);
},
render: function() {
console.log('Rendering Title');
return (
<h2>
{this.heading}
</h2>
);
}
});
module.exports = Title;
Copyright © 2016 M/Gateway Developments Ltd
Title.js
var React = require('react');
var controller;
var Title = React.createClass({
componentWillMount: function() {
controller = require('./Title-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateHeading(newProps.username);
},
render: function() {
console.log('Rendering Title');
return (
<h2>
{this.heading}
</h2>
);
}
});
module.exports = Title;
Fires if props
have changed
Does not fire
on initial render
Copyright © 2016 M/Gateway Developments Ltd
Title-controller.js
module.exports = function (controller, component) {
component.heading = 'My QEWD React Demo';
component.updateHeading = function(username) {
if (username !== '') {
component.heading = 'Welcome, ' + username + ', to ' + component.heading;
}
};
return controller;
};
Copyright © 2016 M/Gateway Developments Ltd
Login.js"use strict"
var React = require('react');
var controller;
var Login = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./Login-controller')(this.props.controller, this);
},
render: function() {
console.log('rendering Login');
if (this.props.loggedIn) {
return (
<div></div>
);
}
return (
<table id="loginTable">
<tbody>
<tr>
<td>Username: </td>
<td><input id="username" /> </td>
</tr>
<tr>
<td>Password: </td>
<td><input id="password" type="password" /> </td>
</tr>
<tr>
<td colSpan="2">
<button
id="LoginBtn"
onClick={this.LoginBtnClicked}
>
Login
</button>
</td>
</tr>
</tbody>
</table>
);
}
});
module.exports = Login;
Copyright © 2016 M/Gateway Developments Ltd
Login.js"use strict"
var React = require('react');
var controller;
var Login = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./Login-controller')(this.props.controller, this);
},
render: function() {
console.log('rendering Login');
if (this.props.loggedIn) {
return (
<div></div>
);
}
return (
<table id="loginTable">
<tbody>
<tr>
<td>Username: </td>
<td><input id="username" /> </td>
</tr>
<tr>
<td>Password: </td>
<td><input id="password" type="password" /> </td>
</tr>
<tr>
<td colSpan="2">
<button
id="LoginBtn"
onClick={this.LoginBtnClicked}
>
Login
</button>
</td>
</tr>
</tbody>
</table>
);
}
});
module.exports = Login;
On successful
login, display
nothing
Form disappears
Copyright © 2016 M/Gateway Developments Ltd
Login.js"use strict"
var React = require('react');
var controller;
var Login = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./Login-controller')(this.props.controller, this);
},
render: function() {
console.log('rendering Login');
if (this.props.loggedIn) {
return (
<div></div>
);
}
return (
<table id="loginTable">
<tbody>
<tr>
<td>Username: </td>
<td><input id="username" /> </td>
</tr>
<tr>
<td>Password: </td>
<td><input id="password" type="password" /> </td>
</tr>
<tr>
<td colSpan="2">
<button
id="LoginBtn"
onClick={this.LoginBtnClicked}
>
Login
</button>
</td>
</tr>
</tbody>
</table>
);
}
});
module.exports = Login;
Otherwise,
display the form
Copyright © 2016 M/Gateway Developments Ltd
Login.js"use strict"
var React = require('react');
var controller;
var Login = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./Login-controller')(this.props.controller, this);
},
render: function() {
console.log('rendering Login');
if (this.props.loggedIn) {
return (
<div></div>
);
}
return (
<table id="loginTable">
<tbody>
<tr>
<td>Username: </td>
<td><input id="username" /> </td>
</tr>
<tr>
<td>Password: </td>
<td><input id="password" type="password" /> </td>
</tr>
<tr>
<td colSpan="2">
<button
id="LoginBtn"
onClick={this.LoginBtnClicked}
>
Login
</button>
</td>
</tr>
</tbody>
</table>
);
}
});
module.exports = Login;
JSX Click handler
Copyright © 2016 M/Gateway Developments Ltd
Login-controller.js
module.exports = function (controller, component) {
component.LoginBtnClicked = function(e) {
var username = $('#username').val();
var password = $('#password').val();
if (username === '') {
alert('You must enter a username!');
return;
}
if (password === '') {
alert('You must enter a password!');
return;
}
controller.send({
type: 'login',
params: {
username: username,
password: password
}
});
};
return controller;
};
Copyright © 2016 M/Gateway Developments Ltd
Login-controller.js
module.exports = function (controller, component) {
component.LoginBtnClicked = function(e) {
var username = $('#username').val();
var password = $('#password').val();
if (username === '') {
alert('You must enter a username!');
return;
}
if (password === '') {
alert('You must enter a password!');
return;
}
controller.send({
type: 'login',
params: {
username: username,
password: password
}
});
};
return controller;
};
Define the
Login button's
click handler
Copyright © 2016 M/Gateway Developments Ltd
Login-controller.js
module.exports = function (controller, component) {
component.LoginBtnClicked = function(e) {
var username = $('#username').val();
var password = $('#password').val();
if (username === '') {
alert('You must enter a username!');
return;
}
if (password === '') {
alert('You must enter a password!');
return;
}
controller.send({
type: 'login',
params: {
username: username,
password: password
}
});
};
return controller;
};
Send the username
and password to
the ewd-xpress
Back-end
The response is
handled in the
Parent component
Copyright © 2016 M/Gateway Developments Ltd
Content.js
var React = require('react');
var controller;
var Content = React.createClass({
componentWillMount: function() {
controller = require('./Content-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateContent(newProps.content);
},
render: function() {
console.log('Rendering Content');
return (
<div>
{this.content}
</div>
);
}
});
module.exports = Content;
Copyright © 2016 M/Gateway Developments Ltd
Content.js
var React = require('react');
var controller;
var Content = React.createClass({
componentWillMount: function() {
controller = require('./Content-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateContent(newProps.content);
},
render: function() {
console.log('Rendering Content');
return (
<div>
{this.content}
</div>
);
}
});
module.exports = Content;
Simply display
the value of
this.content each
Time it renders
Copyright © 2016 M/Gateway Developments Ltd
Content.js
var React = require('react');
var controller;
var Content = React.createClass({
componentWillMount: function() {
controller = require('./Content-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateContent(newProps.content);
},
render: function() {
console.log('Rendering Content');
return (
<div>
{this.content}
</div>
);
}
});
module.exports = Content;
Use the
componentWillReceiveProps
life-cycle method to
update the content
whenever re-rendering
occurs
Copyright © 2016 M/Gateway Developments Ltd
Content.js
var React = require('react');
var controller;
var Content = React.createClass({
componentWillMount: function() {
controller = require('./Content-controller')(this.props.controller, this);
},
componentWillReceiveProps: function(newProps) {
this.updateContent(newProps.content);
},
render: function() {
console.log('Rendering Content');
return (
<div>
{this.content}
</div>
);
}
});
module.exports = Content;
The
componentWillReceiveProps
life-cycle method will
invoke a method named
this.updateContent
We'll define it in the
content-controller module
Copyright © 2016 M/Gateway Developments Ltd
Content-controller.js
module.exports = function (controller, component) {
component.content = component.props.content;
component.updateContent = function(newContent) {
if (newContent !== '') {
component.content = newContent;
}
};
return controller;
};
Copyright © 2016 M/Gateway Developments Ltd
Content-controller.js
module.exports = function (controller, component) {
component.content = component.props.content;
component.updateContent = function(newContent) {
if (newContent !== '') {
component.content = newContent;
}
};
return controller;
};
Here's the
this.updateContent
Method
It just updates
this.content
Within the controller,
instead of this we
refer to component
Copyright © 2016 M/Gateway Developments Ltd
Finally the back-end Module
• This must handle the login message that is
sent from the browser when the Login
form button is clicked
• It must validate the username and
password
• For simplicity, we'll just hard-code the valid
values
– In a real-world app, you'd authenticate against
a database or using a dedicated API
Copyright © 2016 M/Gateway Developments Ltd
Finally the back-end Module
• It's advisable to repeat any browser-side
validation at the back-end
– A malicious user might attempt to send a login
message via the JavaScript console
• We'll authenticate the user and prevent an
already-logged in user from logging in
again
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Repeat the
Browser-side
Validation
Return an error
object if it fails
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Here's our simple
hard-coded validation
Username must be
'rob' and password
must be 'secret'
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Set the session
authenticated
property
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Return a user name
in the response object
This simple example uses
a hard-coded value: 'Rob'
Copyright © 2016 M/Gateway Developments Ltd
That should be it!
• Re-bundle it using Browserify in the
normal way
• Try running it!
Copyright © 2016 M/Gateway Developments Ltd
Try it
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
Initially
Initial Title Display the form No Content
Copyright © 2016 M/Gateway Developments Ltd
Login with wrong credentials
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
On login error
Initial Title Display the form No Content
Display Error
Copyright © 2016 M/Gateway Developments Ltd
Successful login
Username: rob ; password: secret
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
On Successful
login
Hello {{user}}
title
Hide the form Display
Content
Copyright © 2016 M/Gateway Developments Ltd
How that happened
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Send username
& password as EWD
message
Copyright © 2016 M/Gateway Developments Ltd
Login-controller.js
module.exports = function (controller, component) {
component.LoginBtnClicked = function(e) {
var username = $('#username').val();
var password = $('#password').val();
if (username === '') {
alert('You must enter a username!');
return;
}
if (password === '') {
alert('You must enter a password!');
return;
}
controller.send({
type: 'login',
params: {
username: username,
password: password
}
});
};
return controller;
};
Define the
Login button's
click handler
Copyright © 2016 M/Gateway Developments Ltd
Login-controller.js
module.exports = function (controller, component) {
component.LoginBtnClicked = function(e) {
var username = $('#username').val();
var password = $('#password').val();
if (username === '') {
alert('You must enter a username!');
return;
}
if (password === '') {
alert('You must enter a password!');
return;
}
controller.send({
type: 'login',
params: {
username: username,
password: password
}
});
};
return controller;
};
Send the username
and password to
the ewd-xpress
Back-end
The response is
handled in the
Parent component
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Send username
& password as EWD
message
To Back-end
module:
react-demo1.js
Copyright © 2016 M/Gateway Developments Ltd
Back-end module
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You have already logged in'});
return;
}
if (messageObj.params.username === '') {
finished({error: 'You must enter a username'});
return;
}
if (messageObj.params.password === '') {
finished({error: 'You must enter a password'});
return;
}
if (messageObj.params.username !== 'rob') {
finished({error: 'Invalid username'});
return;
}
if (messageObj.params.password !== 'secret') {
finished({error: 'Invalid password'});
return;
}
session.authenticated = true;
finished({
ok: true,
name: 'Rob'
});
}
}
};
~/qewd/node_modules/react-demo1.js
Here's our simple
hard-coded validation
Username must be
'rob' and password
must be 'secret'
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
From
Back-end
Handle response
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
error handler displays
alerts
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Error response
received:
Display Error
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
login handler deals
with successful
logins
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
New values
passed as
props to child
components
if login is
successful
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
component.loggedIn = false;
component.username = '';
component.content = '';
controller.on('error', function(responseObj) {
alert('Error: ' + responseObj.message.error);
});
controller.on('login', function(responseObj) {
if (!responseObj.message.error) {
console.log('Logged in!');
component.loggedIn = true;
component.username = responseObj.message.name;
component.content = 'User has logged in - ready to display some content for the user!';
component.setState({
status: 'loggedIn'
});
}
});
return controller;
};
State value
changed to
trigger
re-render of
this component
and its child
components
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Success response
received:
Re-render
Copyright © 2016 M/Gateway Developments Ltd
MainPage.js
"use strict"
var React = require('react');
var Title = require('./Title');
var Content = require('./Content');
var Login = require('./Login');
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
console.log('Rendering MainPage');
return (
<div>
<Title
controller = {controller}
username = {this.username}
/>
<Login
controller = {controller}
loggedIn = {this.loggedIn}
/>
<Content
controller = {controller}
content = {this.content}
/>
</div>
);
}
});
module.exports = MainPage;
props passed
to child
components
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass down
Username as
prop
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass prop to
Signal successful
login
Copyright © 2016 M/Gateway Developments Ltd
React.js Design
Main Page
Title / Banner Login Form Content
React.js Mechanics
Pass prop to
signal that
content should
be displayed
Copyright © 2016 M/Gateway Developments Ltd
And so we have a working demo!
Username: rob ; password: secret

More Related Content

What's hot

EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...Rob Tweed
 
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternEWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternRob Tweed
 
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...Rob Tweed
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDRob Tweed
 
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationEWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationRob Tweed
 
EWD 3 Training Course Part 30: Modularising QEWD Applications
EWD 3 Training Course Part 30: Modularising QEWD ApplicationsEWD 3 Training Course Part 30: Modularising QEWD Applications
EWD 3 Training Course Part 30: Modularising QEWD ApplicationsRob Tweed
 
EWD 3 Training Course Part 12: QEWD Session Timeout Control
EWD 3 Training Course Part 12: QEWD Session Timeout ControlEWD 3 Training Course Part 12: QEWD Session Timeout Control
EWD 3 Training Course Part 12: QEWD Session Timeout ControlRob Tweed
 
EWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceEWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceRob Tweed
 
EWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsRob Tweed
 
Micronaut For Single Page Apps
Micronaut For Single Page AppsMicronaut For Single Page Apps
Micronaut For Single Page AppsZachary Klein
 
EWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingEWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingRob Tweed
 
Dsc Charusat Learning React Part 1
Dsc Charusat Learning React Part 1 Dsc Charusat Learning React Part 1
Dsc Charusat Learning React Part 1 JainamMehta19
 
Getting Started with React v16
Getting Started with React v16Getting Started with React v16
Getting Started with React v16Benny Neugebauer
 
Fullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year laterFullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year laterMek Srunyu Stittri
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppEdureka!
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideMek Srunyu Stittri
 

What's hot (20)

EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
 
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging PatternEWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
EWD 3 Training Course Part 7: Applying the QEWD Messaging Pattern
 
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWD
 
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User AuthenticationEWD 3 Training Course Part 10: QEWD Sessions and User Authentication
EWD 3 Training Course Part 10: QEWD Sessions and User Authentication
 
EWD 3 Training Course Part 30: Modularising QEWD Applications
EWD 3 Training Course Part 30: Modularising QEWD ApplicationsEWD 3 Training Course Part 30: Modularising QEWD Applications
EWD 3 Training Course Part 30: Modularising QEWD Applications
 
EWD 3 Training Course Part 12: QEWD Session Timeout Control
EWD 3 Training Course Part 12: QEWD Session Timeout ControlEWD 3 Training Course Part 12: QEWD Session Timeout Control
EWD 3 Training Course Part 12: QEWD Session Timeout Control
 
EWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a ServiceEWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a Service
 
EWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIs
 
Micronaut For Single Page Apps
Micronaut For Single Page AppsMicronaut For Single Page Apps
Micronaut For Single Page Apps
 
React js
React jsReact js
React js
 
Angular beans
Angular beansAngular beans
Angular beans
 
EWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session LockingEWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session Locking
 
Dsc Charusat Learning React Part 1
Dsc Charusat Learning React Part 1 Dsc Charusat Learning React Part 1
Dsc Charusat Learning React Part 1
 
Getting Started with React v16
Getting Started with React v16Getting Started with React v16
Getting Started with React v16
 
Fullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year laterFullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year later
 
Node JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web AppNode JS Express : Steps to Create Restful Web App
Node JS Express : Steps to Create Restful Web App
 
Serverless Java on Kubernetes
Serverless Java on KubernetesServerless Java on Kubernetes
Serverless Java on Kubernetes
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java sideNode.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
 
Intro to ReactJS
Intro to ReactJSIntro to ReactJS
Intro to ReactJS
 

Viewers also liked

EWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDEWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDRob Tweed
 
EWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingRob Tweed
 
EWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionEWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionRob Tweed
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDEWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDRob Tweed
 
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global StorageEWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global StorageRob Tweed
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesRob Tweed
 
EWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesRob Tweed
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSEWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSRob Tweed
 
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...Rob Tweed
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsRob Tweed
 
EWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectRob Tweed
 
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSEWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSRob Tweed
 
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...Ontico
 
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsEWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsRob Tweed
 
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesEWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesRob Tweed
 
EWD 3 Training Course Part 17: Introduction to Global Storage Databases
EWD 3 Training Course Part 17: Introduction to Global Storage DatabasesEWD 3 Training Course Part 17: Introduction to Global Storage Databases
EWD 3 Training Course Part 17: Introduction to Global Storage DatabasesRob Tweed
 
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDEWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDRob Tweed
 

Viewers also liked (17)

EWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDEWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWD
 
EWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven Indexing
 
EWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD SessionEWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD Session
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDEWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
 
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global StorageEWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
 
EWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database Capabilities
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSEWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
 
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript Objects
 
EWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode Object
 
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORSEWD 3 Training Course Part 33: Configuring QEWD to use CORS
EWD 3 Training Course Part 33: Configuring QEWD to use CORS
 
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...
Modern Web App Development using ClojureScript & React.js / Baishampayan “BG”...
 
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsEWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
 
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesEWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
 
EWD 3 Training Course Part 17: Introduction to Global Storage Databases
EWD 3 Training Course Part 17: Introduction to Global Storage DatabasesEWD 3 Training Course Part 17: Introduction to Global Storage Databases
EWD 3 Training Course Part 17: Introduction to Global Storage Databases
 
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDEWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
 

Similar to EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5

Mobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveMobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveepamspb
 
Beginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native appBeginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native appKaty Slemon
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend DevelopersSergio Nakamura
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs[T]echdencias
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"GeeksLab Odessa
 
Checkout Customizations in Magento 2 - MageTitansMCR 2017
Checkout Customizations in Magento 2 - MageTitansMCR 2017Checkout Customizations in Magento 2 - MageTitansMCR 2017
Checkout Customizations in Magento 2 - MageTitansMCR 2017Max Pronko
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and SymfonyIgnacio Martín
 
Advanced SharePoint Web Part Development
Advanced SharePoint Web Part DevelopmentAdvanced SharePoint Web Part Development
Advanced SharePoint Web Part DevelopmentRob Windsor
 
Hybrid App using WordPress
Hybrid App using WordPressHybrid App using WordPress
Hybrid App using WordPressHaim Michael
 
Web Components for Java Developers
Web Components for Java DevelopersWeb Components for Java Developers
Web Components for Java DevelopersJoonas Lehtinen
 
WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)MichaelBontyes
 
Apache Aries Blog Sample
Apache Aries Blog SampleApache Aries Blog Sample
Apache Aries Blog SampleSkills Matter
 
The WebView Role in Hybrid Applications
The WebView Role in Hybrid ApplicationsThe WebView Role in Hybrid Applications
The WebView Role in Hybrid ApplicationsHaim Michael
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPresswpnepal
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesRiad Benguella
 
Academy PRO: React JS
Academy PRO: React JSAcademy PRO: React JS
Academy PRO: React JSBinary Studio
 
Academy PRO: React native - building first scenes
Academy PRO: React native - building first scenesAcademy PRO: React native - building first scenes
Academy PRO: React native - building first scenesBinary Studio
 

Similar to EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5 (20)

Mobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast diveMobile Open Day: React Native: Crossplatform fast dive
Mobile Open Day: React Native: Crossplatform fast dive
 
Beginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native appBeginner’s tutorial (part 2) how to integrate redux-saga in react native app
Beginner’s tutorial (part 2) how to integrate redux-saga in react native app
 
Introduction to React for Frontend Developers
Introduction to React for Frontend DevelopersIntroduction to React for Frontend Developers
Introduction to React for Frontend Developers
 
React outbox
React outboxReact outbox
React outbox
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
 
Checkout Customizations in Magento 2 - MageTitansMCR 2017
Checkout Customizations in Magento 2 - MageTitansMCR 2017Checkout Customizations in Magento 2 - MageTitansMCR 2017
Checkout Customizations in Magento 2 - MageTitansMCR 2017
 
Intro react js
Intro react jsIntro react js
Intro react js
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
Advanced SharePoint Web Part Development
Advanced SharePoint Web Part DevelopmentAdvanced SharePoint Web Part Development
Advanced SharePoint Web Part Development
 
Hybrid App using WordPress
Hybrid App using WordPressHybrid App using WordPress
Hybrid App using WordPress
 
Web Components for Java Developers
Web Components for Java DevelopersWeb Components for Java Developers
Web Components for Java Developers
 
WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)WCMTL 15 - Create your own shortcode (Fr)
WCMTL 15 - Create your own shortcode (Fr)
 
Apache Aries Blog Sample
Apache Aries Blog SampleApache Aries Blog Sample
Apache Aries Blog Sample
 
The WebView Role in Hybrid Applications
The WebView Role in Hybrid ApplicationsThe WebView Role in Hybrid Applications
The WebView Role in Hybrid Applications
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisables
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
Academy PRO: React JS
Academy PRO: React JSAcademy PRO: React JS
Academy PRO: React JS
 
Academy PRO: React native - building first scenes
Academy PRO: React native - building first scenesAcademy PRO: React native - building first scenes
Academy PRO: React native - building first scenes
 

More from Rob Tweed

Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language FeatureRob Tweed
 
LNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooLNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooRob Tweed
 
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService FunctionalityEWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService FunctionalityRob Tweed
 
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsEWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsRob Tweed
 
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesEWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesRob Tweed
 
QEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServicesQEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServicesRob Tweed
 
QEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooQEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooRob Tweed
 
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Servicesewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST ServicesRob Tweed
 
qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle TierRob Tweed
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceEWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceRob Tweed
 
EWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeEWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeRob Tweed
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsEWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsRob Tweed
 

More from Rob Tweed (13)

QEWD Update
QEWD UpdateQEWD Update
QEWD Update
 
Data Persistence as a Language Feature
Data Persistence as a Language FeatureData Persistence as a Language Feature
Data Persistence as a Language Feature
 
LNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It TooLNUG: Having Your Node.js Cake and Eating It Too
LNUG: Having Your Node.js Cake and Eating It Too
 
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService FunctionalityEWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality
 
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsEWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
 
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesEWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
 
QEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServicesQEWD.js, JSON Web Tokens & MicroServices
QEWD.js, JSON Web Tokens & MicroServices
 
QEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It TooQEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It Too
 
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Servicesewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
 
qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tier
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker ApplianceEWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker Appliance
 
EWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeEWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient Mode
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsEWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
 

Recently uploaded

chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 

Recently uploaded (20)

chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 

EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5

  • 1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 41 Building a React.js-based QEWD Application (e) Building out a login sequence Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2. Copyright © 2016 M/Gateway Developments Ltd What We Want to Do
  • 3. Copyright © 2016 M/Gateway Developments Ltd What We Want to Do
  • 4. Copyright © 2016 M/Gateway Developments Ltd What We Want to Do
  • 5. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content
  • 6. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content Initially Initial Title Display the form No Content
  • 7. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content On login error Initial Title Display the form No Content Display Error
  • 8. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content On Successful login Hello {{user}} title Hide the form Display Content
  • 9. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Send username & password as EWD message
  • 10. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Send username & password as EWD message To QEWD Back-end
  • 11. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics From Back-end Handle response
  • 12. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Error response received: Display Error
  • 13. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Success response received: Re-render
  • 14. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass down Username as prop
  • 15. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass prop to Signal successful login
  • 16. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass prop to signal that content should be displayed
  • 17. Copyright © 2016 M/Gateway Developments Ltd So let's build out the components In ~/qewd/www/react-demo1
  • 18. Copyright © 2016 M/Gateway Developments Ltd MainPage.js "use strict" var React = require('react'); var Title = require('./Title'); var Content = require('./Content'); var Login = require('./Login'); var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { console.log('Rendering MainPage'); return ( <div> <Title controller = {controller} username = {this.username} /> <Login controller = {controller} loggedIn = {this.loggedIn} /> <Content controller = {controller} content = {this.content} /> </div> ); } }); module.exports = MainPage;
  • 19. Copyright © 2016 M/Gateway Developments Ltd MainPage.js "use strict" var React = require('react'); var Title = require('./Title'); var Content = require('./Content'); var Login = require('./Login'); var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { console.log('Rendering MainPage'); return ( <div> <Title controller = {controller} username = {this.username} /> <Login controller = {controller} loggedIn = {this.loggedIn} /> <Content controller = {controller} content = {this.content} /> </div> ); } }); module.exports = MainPage; Initial state variable value
  • 20. Copyright © 2016 M/Gateway Developments Ltd MainPage.js "use strict" var React = require('react'); var Title = require('./Title'); var Content = require('./Content'); var Login = require('./Login'); var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { console.log('Rendering MainPage'); return ( <div> <Title controller = {controller} username = {this.username} /> <Login controller = {controller} loggedIn = {this.loggedIn} /> <Content controller = {controller} content = {this.content} /> </div> ); } }); module.exports = MainPage; props passed to child components
  • 21. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; };
  • 22. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; Response from back-end handled in this top component
  • 23. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; Initial values passed as props to child components
  • 24. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; New values passed as props to child components if login is successful
  • 25. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; State value changed to trigger re-render of this component and its child components
  • 26. Copyright © 2016 M/Gateway Developments Ltd Title.js var React = require('react'); var controller; var Title = React.createClass({ componentWillMount: function() { controller = require('./Title-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateHeading(newProps.username); }, render: function() { console.log('Rendering Title'); return ( <h2> {this.heading} </h2> ); } }); module.exports = Title;
  • 27. Copyright © 2016 M/Gateway Developments Ltd Title.js var React = require('react'); var controller; var Title = React.createClass({ componentWillMount: function() { controller = require('./Title-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateHeading(newProps.username); }, render: function() { console.log('Rendering Title'); return ( <h2> {this.heading} </h2> ); } }); module.exports = Title; Fires if props have changed Does not fire on initial render
  • 28. Copyright © 2016 M/Gateway Developments Ltd Title-controller.js module.exports = function (controller, component) { component.heading = 'My QEWD React Demo'; component.updateHeading = function(username) { if (username !== '') { component.heading = 'Welcome, ' + username + ', to ' + component.heading; } }; return controller; };
  • 29. Copyright © 2016 M/Gateway Developments Ltd Login.js"use strict" var React = require('react'); var controller; var Login = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./Login-controller')(this.props.controller, this); }, render: function() { console.log('rendering Login'); if (this.props.loggedIn) { return ( <div></div> ); } return ( <table id="loginTable"> <tbody> <tr> <td>Username: </td> <td><input id="username" /> </td> </tr> <tr> <td>Password: </td> <td><input id="password" type="password" /> </td> </tr> <tr> <td colSpan="2"> <button id="LoginBtn" onClick={this.LoginBtnClicked} > Login </button> </td> </tr> </tbody> </table> ); } }); module.exports = Login;
  • 30. Copyright © 2016 M/Gateway Developments Ltd Login.js"use strict" var React = require('react'); var controller; var Login = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./Login-controller')(this.props.controller, this); }, render: function() { console.log('rendering Login'); if (this.props.loggedIn) { return ( <div></div> ); } return ( <table id="loginTable"> <tbody> <tr> <td>Username: </td> <td><input id="username" /> </td> </tr> <tr> <td>Password: </td> <td><input id="password" type="password" /> </td> </tr> <tr> <td colSpan="2"> <button id="LoginBtn" onClick={this.LoginBtnClicked} > Login </button> </td> </tr> </tbody> </table> ); } }); module.exports = Login; On successful login, display nothing Form disappears
  • 31. Copyright © 2016 M/Gateway Developments Ltd Login.js"use strict" var React = require('react'); var controller; var Login = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./Login-controller')(this.props.controller, this); }, render: function() { console.log('rendering Login'); if (this.props.loggedIn) { return ( <div></div> ); } return ( <table id="loginTable"> <tbody> <tr> <td>Username: </td> <td><input id="username" /> </td> </tr> <tr> <td>Password: </td> <td><input id="password" type="password" /> </td> </tr> <tr> <td colSpan="2"> <button id="LoginBtn" onClick={this.LoginBtnClicked} > Login </button> </td> </tr> </tbody> </table> ); } }); module.exports = Login; Otherwise, display the form
  • 32. Copyright © 2016 M/Gateway Developments Ltd Login.js"use strict" var React = require('react'); var controller; var Login = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./Login-controller')(this.props.controller, this); }, render: function() { console.log('rendering Login'); if (this.props.loggedIn) { return ( <div></div> ); } return ( <table id="loginTable"> <tbody> <tr> <td>Username: </td> <td><input id="username" /> </td> </tr> <tr> <td>Password: </td> <td><input id="password" type="password" /> </td> </tr> <tr> <td colSpan="2"> <button id="LoginBtn" onClick={this.LoginBtnClicked} > Login </button> </td> </tr> </tbody> </table> ); } }); module.exports = Login; JSX Click handler
  • 33. Copyright © 2016 M/Gateway Developments Ltd Login-controller.js module.exports = function (controller, component) { component.LoginBtnClicked = function(e) { var username = $('#username').val(); var password = $('#password').val(); if (username === '') { alert('You must enter a username!'); return; } if (password === '') { alert('You must enter a password!'); return; } controller.send({ type: 'login', params: { username: username, password: password } }); }; return controller; };
  • 34. Copyright © 2016 M/Gateway Developments Ltd Login-controller.js module.exports = function (controller, component) { component.LoginBtnClicked = function(e) { var username = $('#username').val(); var password = $('#password').val(); if (username === '') { alert('You must enter a username!'); return; } if (password === '') { alert('You must enter a password!'); return; } controller.send({ type: 'login', params: { username: username, password: password } }); }; return controller; }; Define the Login button's click handler
  • 35. Copyright © 2016 M/Gateway Developments Ltd Login-controller.js module.exports = function (controller, component) { component.LoginBtnClicked = function(e) { var username = $('#username').val(); var password = $('#password').val(); if (username === '') { alert('You must enter a username!'); return; } if (password === '') { alert('You must enter a password!'); return; } controller.send({ type: 'login', params: { username: username, password: password } }); }; return controller; }; Send the username and password to the ewd-xpress Back-end The response is handled in the Parent component
  • 36. Copyright © 2016 M/Gateway Developments Ltd Content.js var React = require('react'); var controller; var Content = React.createClass({ componentWillMount: function() { controller = require('./Content-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateContent(newProps.content); }, render: function() { console.log('Rendering Content'); return ( <div> {this.content} </div> ); } }); module.exports = Content;
  • 37. Copyright © 2016 M/Gateway Developments Ltd Content.js var React = require('react'); var controller; var Content = React.createClass({ componentWillMount: function() { controller = require('./Content-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateContent(newProps.content); }, render: function() { console.log('Rendering Content'); return ( <div> {this.content} </div> ); } }); module.exports = Content; Simply display the value of this.content each Time it renders
  • 38. Copyright © 2016 M/Gateway Developments Ltd Content.js var React = require('react'); var controller; var Content = React.createClass({ componentWillMount: function() { controller = require('./Content-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateContent(newProps.content); }, render: function() { console.log('Rendering Content'); return ( <div> {this.content} </div> ); } }); module.exports = Content; Use the componentWillReceiveProps life-cycle method to update the content whenever re-rendering occurs
  • 39. Copyright © 2016 M/Gateway Developments Ltd Content.js var React = require('react'); var controller; var Content = React.createClass({ componentWillMount: function() { controller = require('./Content-controller')(this.props.controller, this); }, componentWillReceiveProps: function(newProps) { this.updateContent(newProps.content); }, render: function() { console.log('Rendering Content'); return ( <div> {this.content} </div> ); } }); module.exports = Content; The componentWillReceiveProps life-cycle method will invoke a method named this.updateContent We'll define it in the content-controller module
  • 40. Copyright © 2016 M/Gateway Developments Ltd Content-controller.js module.exports = function (controller, component) { component.content = component.props.content; component.updateContent = function(newContent) { if (newContent !== '') { component.content = newContent; } }; return controller; };
  • 41. Copyright © 2016 M/Gateway Developments Ltd Content-controller.js module.exports = function (controller, component) { component.content = component.props.content; component.updateContent = function(newContent) { if (newContent !== '') { component.content = newContent; } }; return controller; }; Here's the this.updateContent Method It just updates this.content Within the controller, instead of this we refer to component
  • 42. Copyright © 2016 M/Gateway Developments Ltd Finally the back-end Module • This must handle the login message that is sent from the browser when the Login form button is clicked • It must validate the username and password • For simplicity, we'll just hard-code the valid values – In a real-world app, you'd authenticate against a database or using a dedicated API
  • 43. Copyright © 2016 M/Gateway Developments Ltd Finally the back-end Module • It's advisable to repeat any browser-side validation at the back-end – A malicious user might attempt to send a login message via the JavaScript console • We'll authenticate the user and prevent an already-logged in user from logging in again
  • 44. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js
  • 45. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js Repeat the Browser-side Validation Return an error object if it fails
  • 46. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js Here's our simple hard-coded validation Username must be 'rob' and password must be 'secret'
  • 47. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js Set the session authenticated property
  • 48. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js Return a user name in the response object This simple example uses a hard-coded value: 'Rob'
  • 49. Copyright © 2016 M/Gateway Developments Ltd That should be it! • Re-bundle it using Browserify in the normal way • Try running it!
  • 50. Copyright © 2016 M/Gateway Developments Ltd Try it
  • 51. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content Initially Initial Title Display the form No Content
  • 52. Copyright © 2016 M/Gateway Developments Ltd Login with wrong credentials
  • 53. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content On login error Initial Title Display the form No Content Display Error
  • 54. Copyright © 2016 M/Gateway Developments Ltd Successful login Username: rob ; password: secret
  • 55. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content On Successful login Hello {{user}} title Hide the form Display Content
  • 56. Copyright © 2016 M/Gateway Developments Ltd How that happened
  • 57. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Send username & password as EWD message
  • 58. Copyright © 2016 M/Gateway Developments Ltd Login-controller.js module.exports = function (controller, component) { component.LoginBtnClicked = function(e) { var username = $('#username').val(); var password = $('#password').val(); if (username === '') { alert('You must enter a username!'); return; } if (password === '') { alert('You must enter a password!'); return; } controller.send({ type: 'login', params: { username: username, password: password } }); }; return controller; }; Define the Login button's click handler
  • 59. Copyright © 2016 M/Gateway Developments Ltd Login-controller.js module.exports = function (controller, component) { component.LoginBtnClicked = function(e) { var username = $('#username').val(); var password = $('#password').val(); if (username === '') { alert('You must enter a username!'); return; } if (password === '') { alert('You must enter a password!'); return; } controller.send({ type: 'login', params: { username: username, password: password } }); }; return controller; }; Send the username and password to the ewd-xpress Back-end The response is handled in the Parent component
  • 60. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Send username & password as EWD message To Back-end module: react-demo1.js
  • 61. Copyright © 2016 M/Gateway Developments Ltd Back-end module module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You have already logged in'}); return; } if (messageObj.params.username === '') { finished({error: 'You must enter a username'}); return; } if (messageObj.params.password === '') { finished({error: 'You must enter a password'}); return; } if (messageObj.params.username !== 'rob') { finished({error: 'Invalid username'}); return; } if (messageObj.params.password !== 'secret') { finished({error: 'Invalid password'}); return; } session.authenticated = true; finished({ ok: true, name: 'Rob' }); } } }; ~/qewd/node_modules/react-demo1.js Here's our simple hard-coded validation Username must be 'rob' and password must be 'secret'
  • 62. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics From Back-end Handle response
  • 63. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; error handler displays alerts
  • 64. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Error response received: Display Error
  • 65. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; login handler deals with successful logins
  • 66. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; New values passed as props to child components if login is successful
  • 67. Copyright © 2016 M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; component.loggedIn = false; component.username = ''; component.content = ''; controller.on('error', function(responseObj) { alert('Error: ' + responseObj.message.error); }); controller.on('login', function(responseObj) { if (!responseObj.message.error) { console.log('Logged in!'); component.loggedIn = true; component.username = responseObj.message.name; component.content = 'User has logged in - ready to display some content for the user!'; component.setState({ status: 'loggedIn' }); } }); return controller; }; State value changed to trigger re-render of this component and its child components
  • 68. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Success response received: Re-render
  • 69. Copyright © 2016 M/Gateway Developments Ltd MainPage.js "use strict" var React = require('react'); var Title = require('./Title'); var Content = require('./Content'); var Login = require('./Login'); var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { console.log('Rendering MainPage'); return ( <div> <Title controller = {controller} username = {this.username} /> <Login controller = {controller} loggedIn = {this.loggedIn} /> <Content controller = {controller} content = {this.content} /> </div> ); } }); module.exports = MainPage; props passed to child components
  • 70. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass down Username as prop
  • 71. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass prop to Signal successful login
  • 72. Copyright © 2016 M/Gateway Developments Ltd React.js Design Main Page Title / Banner Login Form Content React.js Mechanics Pass prop to signal that content should be displayed
  • 73. Copyright © 2016 M/Gateway Developments Ltd And so we have a working demo! Username: rob ; password: secret