Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 40
Building a React.js-based
QEWD Application
(d) Using sub-components
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
Still a very simple app
• Our demo doesn't do very much
• Just a single top-level component
– MainPage.js
• Plus its associated controller module
• React.js apps are usually a hierarchy of
components
– Let's start extending our demo
Copyright © 2016 M/Gateway Developments Ltd
A More Typical Scenario
Main Page
Title / Banner Login Form Main Menu Content
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
// sub-component to handle the Title / banner
// sub-component to handle a log-in form
// sub-component to handle a main menu
// sub-component to handle the main display of content
);
}
});
module.exports = MainPage;
MainPage.js
How it would be implemented:
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
return (
// sub-component to handle the Title / banner
// sub-component to handle the main display of content
);
});
module.exports = MainPage;
MainPage.js
Let's just take 2 for now to
keep it simple:
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title />
<Content />
);
}
});
module.exports = MainPage;
MainPage.js
Let's just take 2 for now to
keep it simple:
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title />
<Content />
);
}
});
module.exports = MainPage;
MainPage.js
These must be
loaded as modules
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
We need to pass the
controller object into them:
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
The child components
are now going to
do the work
The Main Page will no
longer have any
dynamic behaviour as
such, but we'll leave
the controller in place,
but simplify it
Copyright © 2016 M/Gateway Developments Ltd
MainPage-controller.js
module.exports = function (controller, component) {
controller.log = true;
return controller;
};
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
We'll also leave
this state variable
in place
It might come in
useful later
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
So let's create
The first
Sub-component:
<Title />
Copyright © 2016 M/Gateway Developments Ltd
<Title /> Component
var React = require('react');
var heading = 'My QEWD React Demo';
var Title = React.createClass({
render: function() {
return (
<h2>
{heading}
</h2>
);
}
});
module.exports = Title;
Copyright © 2016 M/Gateway Developments Ltd
<Title /> Component
var React = require('react');
var heading = 'My QEWD React Demo';
var Title = React.createClass({
render: function() {
return (
<h2>
{heading}
</h2>
);
}
});
module.exports = Title;
This doesn't require the
controller object for its
current behaviour
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
But we'll still pass
in the controller
for now.
We might need it
later
Copyright © 2016 M/Gateway Developments Ltd
<Title /> Component
var React = require('react');
var heading = 'My QEWD React Demo';
var Title = React.createClass({
render: function() {
return (
<h2>
{heading}
</h2>
);
}
});
module.exports = Title;
If we did want to use it, it
would be referenced as
this.props.controller
Copyright © 2016 M/Gateway Developments Ltd
<Title /> Component
var React = require('react');
var heading = 'My QEWD React Demo';
var Title = React.createClass({
render: function() {
return (
<h2>
{heading}
</h2>
);
}
});
module.exports = Title;
Save as:
~/qewd/www/react-demo1/Title.js
Copyright © 2016 M/Gateway Developments Ltd
Re-bundle and try it again
Copyright © 2016 M/Gateway Developments Ltd
Now let's add the Content Component
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
MainPage.js
Add the Content
Component…
Copyright © 2016 M/Gateway Developments Ltd
<Content /> Component
var React = require('react');
var Content = React.createClass({
render: function() {
return (
<div>
Content will go here...
</div>
);
}
});
module.exports = Content;
We'll keep it very simple for now
No dynamic behaviour yet
Copyright © 2016 M/Gateway Developments Ltd
<Content /> Component
var React = require('react');
var Content = React.createClass({
render: function() {
return (
<div>
Content will go here...
</div>
);
}
});
module.exports = Content;
Save as:
C:ewd3wwwreact-demo1Content.js
Copyright © 2016 M/Gateway Developments Ltd
Re-bundle it
cd ewd3wwwreact-demo1
browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
Copyright © 2016 M/Gateway Developments Ltd
You'll get an error!
[es2015 react] ] app.js > bundle.js
SyntaxError: C:/ewd3-react/www/react-demo3/MainPage.js: Adjacent JSX elements mu
st be wrapped in an enclosing tag (21:6)
19 | controller = {controller}
20 | />
> 21 | <Content
| ^
22 | controller = {controller}
23 | />
24 | );
at Parser.pp.raise (C:ewd3-reactnode_modulesbabelifynode_modulesbabel-c
orenode_modulesbabylonlibparserlocation.js:22:13)
at Parser.pp.jsxParseElementAt (C:ewd3-reactnode_modulesbabelifynode_mod
ulesbabel-corenode_modulesbabylonlibpluginsjsxindex.js:470:10)
Copyright © 2016 M/Gateway Developments Ltd
You'll get an error!
[es2015 react] ] app.js > bundle.js
SyntaxError: C:/ewd3-react/www/react-demo3/MainPage.js: Adjacent JSX elements mu
st be wrapped in an enclosing tag (21:6)
19 | controller = {controller}
20 | />
> 21 | <Content
| ^
22 | controller = {controller}
23 | />
24 | );
at Parser.pp.raise (C:ewd3-reactnode_modulesbabelifynode_modulesbabel-c
orenode_modulesbabylonlibparserlocation.js:22:13)
at Parser.pp.jsxParseElementAt (C:ewd3-reactnode_modulesbabelifynode_mod
ulesbabel-corenode_modulesbabylonlibpluginsjsxindex.js:470:10)
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
Check in MainPage.js
What's inside the return
must only be a single
parent JSX tag
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
);
}
});
module.exports = MainPage;
Check in MainPage.js
What's inside the return
must only be a single
parent JSX tag
So we must put <Title>
and <Content> inside
a parent tag
Copyright © 2016 M/Gateway Developments Ltd
"use strict"
var React = require('react');
var controller;
var Title = require('./Title');
var Content = require('./Content');
var MainPage = React.createClass({
getInitialState: function() {
return {
status: 'initial',
}
},
componentWillMount: function() {
controller = require('./MainPage-controller')(this.props.controller, this);
},
render: function() {
return (
<div>
<Title
controller = {controller}
/>
<Content
controller = {controller}
/>
</div>
);
}
});
module.exports = MainPage;
Check in MainPage.js
A <div> tag will do the job!
Copyright © 2016 M/Gateway Developments Ltd
Now it should re-bundle OK
cd ewd3wwwreact-demo1
browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
Copyright © 2016 M/Gateway Developments Ltd
Try it again
And now we have our
two components working
properly
Copyright © 2016 M/Gateway Developments Ltd
Still just a simple app
• No dynamic behaviour
• Not using the QEWD back-end
• But we've now established the basic
patterns for React development with
QEWD
– The components we've created can be used
as convenient templates
• In the next part of the course, we'll add
some dynamic behaviour

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

  • 1.
    Copyright © 2016M/Gateway Developments Ltd EWD 3 Training Course Part 40 Building a React.js-based QEWD Application (d) Using sub-components Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2.
    Copyright © 2016M/Gateway Developments Ltd Still a very simple app • Our demo doesn't do very much • Just a single top-level component – MainPage.js • Plus its associated controller module • React.js apps are usually a hierarchy of components – Let's start extending our demo
  • 3.
    Copyright © 2016M/Gateway Developments Ltd A More Typical Scenario Main Page Title / Banner Login Form Main Menu Content
  • 4.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( // sub-component to handle the Title / banner // sub-component to handle a log-in form // sub-component to handle a main menu // sub-component to handle the main display of content ); } }); module.exports = MainPage; MainPage.js How it would be implemented:
  • 5.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, return ( // sub-component to handle the Title / banner // sub-component to handle the main display of content ); }); module.exports = MainPage; MainPage.js Let's just take 2 for now to keep it simple:
  • 6.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title /> <Content /> ); } }); module.exports = MainPage; MainPage.js Let's just take 2 for now to keep it simple:
  • 7.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title /> <Content /> ); } }); module.exports = MainPage; MainPage.js These must be loaded as modules
  • 8.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js We need to pass the controller object into them:
  • 9.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js The child components are now going to do the work The Main Page will no longer have any dynamic behaviour as such, but we'll leave the controller in place, but simplify it
  • 10.
    Copyright © 2016M/Gateway Developments Ltd MainPage-controller.js module.exports = function (controller, component) { controller.log = true; return controller; };
  • 11.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js We'll also leave this state variable in place It might come in useful later
  • 12.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js So let's create The first Sub-component: <Title />
  • 13.
    Copyright © 2016M/Gateway Developments Ltd <Title /> Component var React = require('react'); var heading = 'My QEWD React Demo'; var Title = React.createClass({ render: function() { return ( <h2> {heading} </h2> ); } }); module.exports = Title;
  • 14.
    Copyright © 2016M/Gateway Developments Ltd <Title /> Component var React = require('react'); var heading = 'My QEWD React Demo'; var Title = React.createClass({ render: function() { return ( <h2> {heading} </h2> ); } }); module.exports = Title; This doesn't require the controller object for its current behaviour
  • 15.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js But we'll still pass in the controller for now. We might need it later
  • 16.
    Copyright © 2016M/Gateway Developments Ltd <Title /> Component var React = require('react'); var heading = 'My QEWD React Demo'; var Title = React.createClass({ render: function() { return ( <h2> {heading} </h2> ); } }); module.exports = Title; If we did want to use it, it would be referenced as this.props.controller
  • 17.
    Copyright © 2016M/Gateway Developments Ltd <Title /> Component var React = require('react'); var heading = 'My QEWD React Demo'; var Title = React.createClass({ render: function() { return ( <h2> {heading} </h2> ); } }); module.exports = Title; Save as: ~/qewd/www/react-demo1/Title.js
  • 18.
    Copyright © 2016M/Gateway Developments Ltd Re-bundle and try it again
  • 19.
    Copyright © 2016M/Gateway Developments Ltd Now let's add the Content Component
  • 20.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; MainPage.js Add the Content Component…
  • 21.
    Copyright © 2016M/Gateway Developments Ltd <Content /> Component var React = require('react'); var Content = React.createClass({ render: function() { return ( <div> Content will go here... </div> ); } }); module.exports = Content; We'll keep it very simple for now No dynamic behaviour yet
  • 22.
    Copyright © 2016M/Gateway Developments Ltd <Content /> Component var React = require('react'); var Content = React.createClass({ render: function() { return ( <div> Content will go here... </div> ); } }); module.exports = Content; Save as: C:ewd3wwwreact-demo1Content.js
  • 23.
    Copyright © 2016M/Gateway Developments Ltd Re-bundle it cd ewd3wwwreact-demo1 browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
  • 24.
    Copyright © 2016M/Gateway Developments Ltd You'll get an error! [es2015 react] ] app.js > bundle.js SyntaxError: C:/ewd3-react/www/react-demo3/MainPage.js: Adjacent JSX elements mu st be wrapped in an enclosing tag (21:6) 19 | controller = {controller} 20 | /> > 21 | <Content | ^ 22 | controller = {controller} 23 | /> 24 | ); at Parser.pp.raise (C:ewd3-reactnode_modulesbabelifynode_modulesbabel-c orenode_modulesbabylonlibparserlocation.js:22:13) at Parser.pp.jsxParseElementAt (C:ewd3-reactnode_modulesbabelifynode_mod ulesbabel-corenode_modulesbabylonlibpluginsjsxindex.js:470:10)
  • 25.
    Copyright © 2016M/Gateway Developments Ltd You'll get an error! [es2015 react] ] app.js > bundle.js SyntaxError: C:/ewd3-react/www/react-demo3/MainPage.js: Adjacent JSX elements mu st be wrapped in an enclosing tag (21:6) 19 | controller = {controller} 20 | /> > 21 | <Content | ^ 22 | controller = {controller} 23 | /> 24 | ); at Parser.pp.raise (C:ewd3-reactnode_modulesbabelifynode_modulesbabel-c orenode_modulesbabylonlibparserlocation.js:22:13) at Parser.pp.jsxParseElementAt (C:ewd3-reactnode_modulesbabelifynode_mod ulesbabel-corenode_modulesbabylonlibpluginsjsxindex.js:470:10)
  • 26.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; Check in MainPage.js What's inside the return must only be a single parent JSX tag
  • 27.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <Title controller = {controller} /> <Content controller = {controller} /> ); } }); module.exports = MainPage; Check in MainPage.js What's inside the return must only be a single parent JSX tag So we must put <Title> and <Content> inside a parent tag
  • 28.
    Copyright © 2016M/Gateway Developments Ltd "use strict" var React = require('react'); var controller; var Title = require('./Title'); var Content = require('./Content'); var MainPage = React.createClass({ getInitialState: function() { return { status: 'initial', } }, componentWillMount: function() { controller = require('./MainPage-controller')(this.props.controller, this); }, render: function() { return ( <div> <Title controller = {controller} /> <Content controller = {controller} /> </div> ); } }); module.exports = MainPage; Check in MainPage.js A <div> tag will do the job!
  • 29.
    Copyright © 2016M/Gateway Developments Ltd Now it should re-bundle OK cd ewd3wwwreact-demo1 browserify -t [ babelify --compact false --presets [es2015 react] ] app.js > bundle.js
  • 30.
    Copyright © 2016M/Gateway Developments Ltd Try it again And now we have our two components working properly
  • 31.
    Copyright © 2016M/Gateway Developments Ltd Still just a simple app • No dynamic behaviour • Not using the QEWD back-end • But we've now established the basic patterns for React development with QEWD – The components we've created can be used as convenient templates • In the next part of the course, we'll add some dynamic behaviour