Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 38
Building a React.js-based QEWD
Application
(b) First steps in developing
with React.js
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
Here's our Main component
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Let's make it send a message
to QEWD and return
a response that we display
Copyright © 2016 M/Gateway Developments Ltd
Here's our Main component
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
We'll display the returned message here
</div>
);
}
});
module.exports = MainPage;
Let's make it send a message
To QEWD and return
a response that we display
Copyright © 2016 M/Gateway Developments Ltd
When and where to send a message?
We're currently just using the
component's render() function
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
When and where to send a message?
To send a message to QEWD,
we should use
a React life-cycle method
For example:
componentWillMount()
componentDidMount()
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
When and where to send a message?
To send a message to QEWD,
we should use
a React life-cycle method
For example:
componentWillMount()
Triggered before rendering
takes place
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
When and where to send a message?
To send a message to QEWD,
we should use
a React life-cycle method
For example:
componentDidMount()
Triggered after rendering
takes place
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
When and where to send a message?
To send a message to QEWD,
we should use
a React life-cycle method
For example:
componentWillMount()
Triggered before rendering
takes place
This would seem to be the
obvious one to use in our example
~/qewd/www/react-demo1/MainPage.js
"use strict"
var React = require('react');
var MainPage = React.createClass({
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
componentWillMount()
"use strict"
var React = require('react');
module.exports = React.createClass({
componentWillMount: function() {
// do something before it renders
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
componentWillMount()
The controller object is created
from and extends the EWD object
So to send a message, we invoke
its send() method
The controller was passed to
our main component as a prop
named controller, so we
refer to it as this.props.controller
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
Re-bundle it
cd ewd3wwwreact-demo1
browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
You must re-run Browserify (or WebPack) to create a new bundle.js file, every
time you modify any component or module in your application
Copyright © 2016 M/Gateway Developments Ltd
Try it
Nothing seems to have happened!
Copyright © 2016 M/Gateway Developments Ltd
But check the QEWD log..
worker 5036 received message: {"type":"testMessage","params":{"foo":"bar"},"toke
n":"9e27f30e-7152-469b-90fe-d637e5e1d8db"}
master process received response from worker 5036: {"type":"testMessage","finish
ed":true,"message":{"error":"No handler defined for react-demo1 messages of type
testMessage"}}
*** handleMessage response {"type":"testMessage","finished":true,"message":{"err
or":"No handler defined for react-demo1 messages of type testMessage"}}
sending to socket /#nDzlXWbHSfB2zCT5AAAH
Master process has finished processing response from worker process 5036 which i
s back in available pool
Copyright © 2016 M/Gateway Developments Ltd
So it did send a message
• Nothing is showing in the browser
because logging isn't enabled
• Let's fix that….
Copyright © 2016 M/Gateway Developments Ltd
Turn on logging
This is the equivalent of
EWD.log = true;
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is my test React.js Application
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
Re-bundle it
browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
Copyright © 2016 M/Gateway Developments Ltd
Try it
Now we can see that the message
was sent and a response
received
Copyright © 2016 M/Gateway Developments Ltd
Try it
But we can also see that we're
getting back an error
Of course, that's because we
haven't created a back-end
handler module for this
application
Copyright © 2016 M/Gateway Developments Ltd
We'll fix that…
~/qewd/node_modules/react-demo1.js
module.exports = {
handlers: {
testMessage: function(messageObj, session, send, finished) {
finished({text: 'Welcome! The value of foo was ' + messageObj.params.foo});
}
}
};
It's just a standard QEWD back-end handler module
Copyright © 2016 M/Gateway Developments Ltd
Try it again
Now it's working
But we're not displaying the
response in the page yet
Copyright © 2016 M/Gateway Developments Ltd
Displaying the message
So how do we get the response
message to display here?
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is where we want to display the response message
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
We seem to have a problem
• Look carefully at the browser's console
log:
react-demo1 registered
sent: {"type":"testMessage","params":{"foo":"bar"}}
rendering MainPage
received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
Copyright © 2016 M/Gateway Developments Ltd
We seem to have a problem
• Look carefully at the browser's console
log:
react-demo1 registered
sent: {"type":"testMessage","params":{"foo":"bar"}}
rendering MainPage
received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
We sent the message to the back-end
Copyright © 2016 M/Gateway Developments Ltd
We seem to have a problem
• Look carefully at the browser's console
log:
react-demo1 registered
sent: {"type":"testMessage","params":{"foo":"bar"}}
rendering MainPage
received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
But before the response was received
Copyright © 2016 M/Gateway Developments Ltd
We seem to have a problem
• Look carefully at the browser's console
log:
react-demo1 registered
sent: {"type":"testMessage","params":{"foo":"bar"}}
rendering MainPage
received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
The Main Page render() function ran
Copyright © 2016 M/Gateway Developments Ltd
We seem to have a problem
• Look carefully at the browser's console
log:
react-demo1 registered
sent: {"type":"testMessage","params":{"foo":"bar"}}
rendering MainPage
received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
The Main Page render() function ran
..and React gives us no way to
prevent that
Copyright © 2016 M/Gateway Developments Ltd
Displaying the message
So how do we deal with this?
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is where we want to display the response message
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
By using a state variable
Have a conditional render()
outcome dependent on the
value of a state variable
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
This is where we want to display the response message
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
By using a state variable
The state variable is initialised
with one value, causing
an initial rendering
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
Initial message....probably nothing at all
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
By using a state variable
When the response is received
change the state variable value
and make the render()
function now display the
response message
"use strict"
var React = require('react');
var MainPage = React.createClass({
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>
Welcome! The value of foo was bar
</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
this.props.controller.send({
type: 'testMessage',
params: {
foo: 'bar'
}
});
},
...etc
Create a state variable
named status with
and initial value of initial
The getInitialState() method
is provided by React for
this purpose
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
this.props.controller.send(message, function(responseObj) {
// handle the returned response
});
},
Modify the message-
sending logic to include a
response handler
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
component.setState({status: responseObj.message.text});
});
},
Change the state variable
and give it the value of
the response text
React provides this.setState
for this purpose
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
component.setState({status: responseObj.message.text});
});
},
We need to ensure
the external value of
this is correctly available
within the callback's closure
Copyright © 2016 M/Gateway Developments Ltd
That's the state variable working
• Now we need to make the component's
render() method conditional on its value…
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
..etc..
render: function() {
console.log('rendering MainPage');
if (this.state.status === 'initial') {
return (
<div></div>
);
}
else {
return (
<div>{this.state.status}</div>
);
}
}
..etc
So it now renders an empty
div inititially
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
..etc..
render: function() {
console.log('rendering MainPage');
if (this.state.status === 'initial') {
return (
<div></div>
);
}
else {
return (
<div>{this.state.status}</div>
);
}
}
..etc
Then, when re-rendered,
it displays the value of
the state variable
which should now contain
the response text
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
..etc..
var component = this;
this.props.controller.send(message, function(responseObj) {
component.setState({status: responseObj.message.text});
});
},
render: function() {
console.log('rendering MainPage');
if (this.state.status === 'initial') {
return (
<div></div>
);
}
else {
return (
<div>{this.state.status}</div>
);
}
}
..etc
Re-rendering occurs
automatically when the
state variable value is
changed within the
response handler's
callback function
Copyright © 2016 M/Gateway Developments Ltd
Do the following…
..etc..
render: function() {
console.log('rendering MainPage');
if (this.state.status === 'initial') {
return (
<div></div>
);
}
else {
return (
<div>{this.state.status}</div>
);
}
}
..etc
Note how JSX variables are
specified within
curly braces
Copyright © 2016 M/Gateway Developments Ltd
Try it again
The returned message
is now displayed in the page
You may briefly see "Please wait"
appearing during the registration
process
Copyright © 2016 M/Gateway Developments Ltd
Try it again
Notice how MainPage
Is rendered twice
Once initially
then again when the
response is received
Copyright © 2016 M/Gateway Developments Ltd
Which life-cycle method to use?
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
component.setState({status: responseObj.message.text});
});
},
We used componentWillMount()
which fires before our
MainPage component is
rendered.
Copyright © 2016 M/Gateway Developments Ltd
Which life-cycle method to use?
"use strict"
var React = require('react');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentDidMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
component.setState({status: responseObj.message.text});
});
},
We could have used
componentDidMount()
However this would have
slightly delayed
sending the
message to QEWD
until after the
initial render
was completed
Copyright © 2016 M/Gateway Developments Ltd
Keep state variables for state only?
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
Use a simple variable
for the display text,
initially set to an empty
string
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
The render() method
is no longer
conditional, but
now simply
displays the value
of the displayText
variable
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
The status
State variable is
Still initialised as
before
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
The response
handler now
resets the value
of the displayText
variable to the
received response
text
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
And then changes
the status state
variable to some
other arbitrary value
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
The state variable
is now just being
used as a trigger
to force a
re-render of the
MainPage
component
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
On the 1st
render, displayText
is an empty
string so nothing
appears in the browser
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var displayText = '';
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
this.props.controller.log = true;
var message = {
type: 'testMessage',
params: {
foo: 'bar'
}
};
var component = this;
this.props.controller.send(message, function(responseObj) {
displayText = responseObj.message.text;
component.setState({status: 'updated'});
});
},
render: function() {
console.log('rendering MainPage');
return (
<div>{displayText}</div>
);
}
});
module.exports = MainPage;
But on the 2nd
render, displayText
now contains the
returned response
Copyright © 2016 M/Gateway Developments Ltd
Re-bundle and try it again
It will run exactly
the same as before
Copyright © 2016 M/Gateway Developments Ltd
Separation of Concerns
• Currently they aren't separated
– The dynamic behaviour and its associated
controlling logic is all included in the
MainPage component
– MainPage should ideally just describe the
View logic
Copyright © 2016 M/Gateway Developments Ltd
Separation of Concerns
• Currently they aren't separated
– The dynamic behaviour and its associated
controlling logic is all included in the
MainPage component
– MainPage should ideally just describe the
View logic
• In the next part of this course we'll look at
how we can separate things

EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2

  • 1.
    Copyright © 2016M/Gateway Developments Ltd EWD 3 Training Course Part 38 Building a React.js-based QEWD Application (b) First steps in developing with React.js Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2.
    Copyright © 2016M/Gateway Developments Ltd Here's our Main component ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage; Let's make it send a message to QEWD and return a response that we display
  • 3.
    Copyright © 2016M/Gateway Developments Ltd Here's our Main component ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> We'll display the returned message here </div> ); } }); module.exports = MainPage; Let's make it send a message To QEWD and return a response that we display
  • 4.
    Copyright © 2016M/Gateway Developments Ltd When and where to send a message? We're currently just using the component's render() function ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 5.
    Copyright © 2016M/Gateway Developments Ltd When and where to send a message? To send a message to QEWD, we should use a React life-cycle method For example: componentWillMount() componentDidMount() ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 6.
    Copyright © 2016M/Gateway Developments Ltd When and where to send a message? To send a message to QEWD, we should use a React life-cycle method For example: componentWillMount() Triggered before rendering takes place ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 7.
    Copyright © 2016M/Gateway Developments Ltd When and where to send a message? To send a message to QEWD, we should use a React life-cycle method For example: componentDidMount() Triggered after rendering takes place ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 8.
    Copyright © 2016M/Gateway Developments Ltd When and where to send a message? To send a message to QEWD, we should use a React life-cycle method For example: componentWillMount() Triggered before rendering takes place This would seem to be the obvious one to use in our example ~/qewd/www/react-demo1/MainPage.js "use strict" var React = require('react'); var MainPage = React.createClass({ render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 9.
    Copyright © 2016M/Gateway Developments Ltd componentWillMount() "use strict" var React = require('react'); module.exports = React.createClass({ componentWillMount: function() { // do something before it renders }, render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 10.
    Copyright © 2016M/Gateway Developments Ltd componentWillMount() The controller object is created from and extends the EWD object So to send a message, we invoke its send() method The controller was passed to our main component as a prop named controller, so we refer to it as this.props.controller "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 11.
    Copyright © 2016M/Gateway Developments Ltd Re-bundle it cd ewd3wwwreact-demo1 browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js You must re-run Browserify (or WebPack) to create a new bundle.js file, every time you modify any component or module in your application
  • 12.
    Copyright © 2016M/Gateway Developments Ltd Try it Nothing seems to have happened!
  • 13.
    Copyright © 2016M/Gateway Developments Ltd But check the QEWD log.. worker 5036 received message: {"type":"testMessage","params":{"foo":"bar"},"toke n":"9e27f30e-7152-469b-90fe-d637e5e1d8db"} master process received response from worker 5036: {"type":"testMessage","finish ed":true,"message":{"error":"No handler defined for react-demo1 messages of type testMessage"}} *** handleMessage response {"type":"testMessage","finished":true,"message":{"err or":"No handler defined for react-demo1 messages of type testMessage"}} sending to socket /#nDzlXWbHSfB2zCT5AAAH Master process has finished processing response from worker process 5036 which i s back in available pool
  • 14.
    Copyright © 2016M/Gateway Developments Ltd So it did send a message • Nothing is showing in the browser because logging isn't enabled • Let's fix that….
  • 15.
    Copyright © 2016M/Gateway Developments Ltd Turn on logging This is the equivalent of EWD.log = true; "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> This is my test React.js Application </div> ); } }); module.exports = MainPage;
  • 16.
    Copyright © 2016M/Gateway Developments Ltd Re-bundle it browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
  • 17.
    Copyright © 2016M/Gateway Developments Ltd Try it Now we can see that the message was sent and a response received
  • 18.
    Copyright © 2016M/Gateway Developments Ltd Try it But we can also see that we're getting back an error Of course, that's because we haven't created a back-end handler module for this application
  • 19.
    Copyright © 2016M/Gateway Developments Ltd We'll fix that… ~/qewd/node_modules/react-demo1.js module.exports = { handlers: { testMessage: function(messageObj, session, send, finished) { finished({text: 'Welcome! The value of foo was ' + messageObj.params.foo}); } } }; It's just a standard QEWD back-end handler module
  • 20.
    Copyright © 2016M/Gateway Developments Ltd Try it again Now it's working But we're not displaying the response in the page yet
  • 21.
    Copyright © 2016M/Gateway Developments Ltd Displaying the message So how do we get the response message to display here? "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> This is where we want to display the response message </div> ); } }); module.exports = MainPage;
  • 22.
    Copyright © 2016M/Gateway Developments Ltd We seem to have a problem • Look carefully at the browser's console log: react-demo1 registered sent: {"type":"testMessage","params":{"foo":"bar"}} rendering MainPage received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}}
  • 23.
    Copyright © 2016M/Gateway Developments Ltd We seem to have a problem • Look carefully at the browser's console log: react-demo1 registered sent: {"type":"testMessage","params":{"foo":"bar"}} rendering MainPage received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}} We sent the message to the back-end
  • 24.
    Copyright © 2016M/Gateway Developments Ltd We seem to have a problem • Look carefully at the browser's console log: react-demo1 registered sent: {"type":"testMessage","params":{"foo":"bar"}} rendering MainPage received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}} But before the response was received
  • 25.
    Copyright © 2016M/Gateway Developments Ltd We seem to have a problem • Look carefully at the browser's console log: react-demo1 registered sent: {"type":"testMessage","params":{"foo":"bar"}} rendering MainPage received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}} The Main Page render() function ran
  • 26.
    Copyright © 2016M/Gateway Developments Ltd We seem to have a problem • Look carefully at the browser's console log: react-demo1 registered sent: {"type":"testMessage","params":{"foo":"bar"}} rendering MainPage received: {"type":"testMessage","finished":true,"message":{"text":"Welcome! The value of foo was bar"}} The Main Page render() function ran ..and React gives us no way to prevent that
  • 27.
    Copyright © 2016M/Gateway Developments Ltd Displaying the message So how do we deal with this? "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> This is where we want to display the response message </div> ); } }); module.exports = MainPage;
  • 28.
    Copyright © 2016M/Gateway Developments Ltd By using a state variable Have a conditional render() outcome dependent on the value of a state variable "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> This is where we want to display the response message </div> ); } }); module.exports = MainPage;
  • 29.
    Copyright © 2016M/Gateway Developments Ltd By using a state variable The state variable is initialised with one value, causing an initial rendering "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> Initial message....probably nothing at all </div> ); } }); module.exports = MainPage;
  • 30.
    Copyright © 2016M/Gateway Developments Ltd By using a state variable When the response is received change the state variable value and make the render() function now display the response message "use strict" var React = require('react'); var MainPage = React.createClass({ componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, render: function() { console.log('rendering MainPage'); return ( <div> Welcome! The value of foo was bar </div> ); } }); module.exports = MainPage;
  • 31.
    Copyright © 2016M/Gateway Developments Ltd Do the following… "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; this.props.controller.send({ type: 'testMessage', params: { foo: 'bar' } }); }, ...etc Create a state variable named status with and initial value of initial The getInitialState() method is provided by React for this purpose
  • 32.
    Copyright © 2016M/Gateway Developments Ltd Do the following… "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; this.props.controller.send(message, function(responseObj) { // handle the returned response }); }, Modify the message- sending logic to include a response handler
  • 33.
    Copyright © 2016M/Gateway Developments Ltd Do the following… "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { component.setState({status: responseObj.message.text}); }); }, Change the state variable and give it the value of the response text React provides this.setState for this purpose
  • 34.
    Copyright © 2016M/Gateway Developments Ltd Do the following… "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { component.setState({status: responseObj.message.text}); }); }, We need to ensure the external value of this is correctly available within the callback's closure
  • 35.
    Copyright © 2016M/Gateway Developments Ltd That's the state variable working • Now we need to make the component's render() method conditional on its value…
  • 36.
    Copyright © 2016M/Gateway Developments Ltd Do the following… ..etc.. render: function() { console.log('rendering MainPage'); if (this.state.status === 'initial') { return ( <div></div> ); } else { return ( <div>{this.state.status}</div> ); } } ..etc So it now renders an empty div inititially
  • 37.
    Copyright © 2016M/Gateway Developments Ltd Do the following… ..etc.. render: function() { console.log('rendering MainPage'); if (this.state.status === 'initial') { return ( <div></div> ); } else { return ( <div>{this.state.status}</div> ); } } ..etc Then, when re-rendered, it displays the value of the state variable which should now contain the response text
  • 38.
    Copyright © 2016M/Gateway Developments Ltd Do the following… ..etc.. var component = this; this.props.controller.send(message, function(responseObj) { component.setState({status: responseObj.message.text}); }); }, render: function() { console.log('rendering MainPage'); if (this.state.status === 'initial') { return ( <div></div> ); } else { return ( <div>{this.state.status}</div> ); } } ..etc Re-rendering occurs automatically when the state variable value is changed within the response handler's callback function
  • 39.
    Copyright © 2016M/Gateway Developments Ltd Do the following… ..etc.. render: function() { console.log('rendering MainPage'); if (this.state.status === 'initial') { return ( <div></div> ); } else { return ( <div>{this.state.status}</div> ); } } ..etc Note how JSX variables are specified within curly braces
  • 40.
    Copyright © 2016M/Gateway Developments Ltd Try it again The returned message is now displayed in the page You may briefly see "Please wait" appearing during the registration process
  • 41.
    Copyright © 2016M/Gateway Developments Ltd Try it again Notice how MainPage Is rendered twice Once initially then again when the response is received
  • 42.
    Copyright © 2016M/Gateway Developments Ltd Which life-cycle method to use? "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { component.setState({status: responseObj.message.text}); }); }, We used componentWillMount() which fires before our MainPage component is rendered.
  • 43.
    Copyright © 2016M/Gateway Developments Ltd Which life-cycle method to use? "use strict" var React = require('react'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentDidMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { component.setState({status: responseObj.message.text}); }); }, We could have used componentDidMount() However this would have slightly delayed sending the message to QEWD until after the initial render was completed
  • 44.
    Copyright © 2016M/Gateway Developments Ltd Keep state variables for state only?
  • 45.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage;
  • 46.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; Use a simple variable for the display text, initially set to an empty string
  • 47.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; The render() method is no longer conditional, but now simply displays the value of the displayText variable
  • 48.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; The status State variable is Still initialised as before
  • 49.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; The response handler now resets the value of the displayText variable to the received response text
  • 50.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; And then changes the status state variable to some other arbitrary value
  • 51.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; The state variable is now just being used as a trigger to force a re-render of the MainPage component
  • 52.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; On the 1st render, displayText is an empty string so nothing appears in the browser
  • 53.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var displayText = ''; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { this.props.controller.log = true; var message = { type: 'testMessage', params: { foo: 'bar' } }; var component = this; this.props.controller.send(message, function(responseObj) { displayText = responseObj.message.text; component.setState({status: 'updated'}); }); }, render: function() { console.log('rendering MainPage'); return ( <div>{displayText}</div> ); } }); module.exports = MainPage; But on the 2nd render, displayText now contains the returned response
  • 54.
    Copyright © 2016M/Gateway Developments Ltd Re-bundle and try it again It will run exactly the same as before
  • 55.
    Copyright © 2016M/Gateway Developments Ltd Separation of Concerns • Currently they aren't separated – The dynamic behaviour and its associated controlling logic is all included in the MainPage component – MainPage should ideally just describe the View logic
  • 56.
    Copyright © 2016M/Gateway Developments Ltd Separation of Concerns • Currently they aren't separated – The dynamic behaviour and its associated controlling logic is all included in the MainPage component – MainPage should ideally just describe the View logic • In the next part of this course we'll look at how we can separate things