Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bring your web application architecture to the next level with React.JS

Bring your web application architecture to the next level with React.JS

React.JS is a very powerful library for building user interfaces and the new, cool kid in the JavaScript world. But it is also very interesting for creating a fast, maintainable and interactive web application. In this talk I'll show what React is all about and how you can combine it’s power with your web application, even if you have to render out HTML on the server side. The result will be a flexible, component based architecture that enables developers to take page elements, like a single "follow" button, and place it everywhere on the site without having to duplicate any backend code, JS, HTML, CSS or AJAX endpoints in the process.

Bastian Hofmann

June 15, 2015
Tweet

More Decks by Bastian Hofmann

Other Decks in Programming

Transcript

  1. de duplication ode duplication code duplication code duplication code duplication

    code duplication code duplication code duplication code duplication code duplication code duplication code duplication code duplication code duplicatio code duplicat code duplic code dup code du code cod co co
  2. So?

  3. MVC

  4. var HelloMessage = React.createClass(
 {
 render: function () {
 return

    <div>Hello {this.props.name}</div>;
 }
 }
 );
 
 React.render(<HelloMessage name="John" />, mountNode);
  5. var Timer = React.createClass({
 getInitialState: function () {
 return {secs:

    0};
 },
 tick: function () {
 this.setState({secs: this.state.secs + 1});
 },
 componentDidMount: function () {
 this.interval = setInterval(this.tick, 1000);
 },
 componentWillUnmount: function () {
 clearInterval(this.interval);
 },
 render: function () {
 return (<div> Seconds Elapsed: {this.state.secs}</div> );
 }
 });
 
 React.render(<Timer />, mountNode);
  6. var TodoList = React.createClass(
 {
 render: function () {
 var

    createItem = function (itemText) {
 return <li>{itemText}</li>;
 };
 return ( <ul> {this.props.items.map(createItem)} </ul> );
 }
 }
 );
  7. var TodoApp = React.createClass({
 getInitialState: function () {
 return {items:

    […]};
 },
 render: function () {
 return (
 <div>
 <h3>TODO</h3>
 <TodoList items={this.state.items} />
 </div>
 );
 }
 });
  8. Profile Publications Publication Publication Publication AboutMe LeftColumn Image Menu Account

    Account Account Account Account Publication1 Publication2 Publication3 Institution
  9. Widget Widget Widget Widget Preparer Resolver Resolver Services Connector Interfaces

    Connector Implementations Batch requirements and pass them to resolvers
  10. Widget Widget Widget Widget Preparer Resolver Resolver Services Connector Interfaces

    Connector Implementations Call Services as effective as possible (Multi-GET,...)
  11. Widget Widget Widget Widget Preparer Resolver Resolver Services Connector Interfaces

    Connector Implementations Attach fetched data to Requirements and pass them back to the preparer
  12. Widget Widget Widget Widget Preparer Resolver Resolver Services Connector Interfaces

    Connector Implementations Distribute fetched data to the widgets that required it
  13. public function collect() {
 return new RequirementCollection([
 new EntityRequirement(
 'account',


    Account::class,
 ['id' => $this->request->get('id')]
 ),
 ], function() {
 return new RequirementCollection([
 new ServiceRequirement(
 'scienceDisciplines',
 AccountService::class,
 'getScienceDisciplines',
 ['account' => $this->account]
 )
 ]);
 });
 }
  14. public function collect() {
 yield [
 new EntityRequirement(
 'account',
 Account::class,


    ['id' => $this->request->get('id')]
 ),
 ];
 
 yield [
 new ServiceRequirement(
 'scienceDisciplines',
 AccountService::class,
 'getScienceDisciplines',
 ['account' => $this->account]
 )
 ];
 }
  15. trait ServiceRequirementFactory {
 
 /** @return $this */
 public static

    function getCall() {
 return new ServiceRequirementFactoryProxy( static::class );
 }
 }
  16. class ServiceRequirementFactoryProxy {
 
 private $className;
 
 public function __construct($className)

    {
 $this->className = $className;
 }
 
 public function __call($name, $arguments) {
 return [
 'serviceClass' => $this->className,
 'methodName' => $name,
 'arguments' => $arguments
 ];
 }
 
 }
  17. class PublicationKeywordSearch {
 
 public $publications;
 public $publicationListItems = [];


    
 public function collect() {
 yield [
 serviceRequirement(
 'publications',
 PublicationService::getCall()->getByKeywords(
 ['virology', 'cancer'], 10, 0
 )
 )
 ];
 foreach ($this->publications as $publication) {
 yield new WidgetRequirement(
 'publicationListItems',
 PublicationItem::CLASS,
 [ 'publicationId' => $publication->getId() ]
 );
 }
 }
 }
  18. class PublicationItem {
 
 public $publicationId;
 public $publication;
 
 public

    function collect() {
 yield new RequestDataRequirement('publicationId');
 yield [
 new EntityRequirement(
 'publication',
 Publication::class,
 ['id' => $this->publicationId]
 )
 ];
 }
 }
  19. public function getData() {
 return [
 'key' => 'value',
 'other'

    => 'data',
 'number' => 42,
 'subComponent' => $this->subComponent,
 'someArray' => [1, 2, 3],
 'subComponentList' => [ $this->items[0], $this->items[1] ]
 ];
 }
  20. {
 "component": "ParentComponent.jsx",
 "id": "ParentComponent-1234",
 "data": {
 "key": "value",
 "other":

    "data",
 "number": 42,
 "subComponent": {
 "component": "SubComponent.jsx",
 "id": "SubComponent",
 "data": { "boolValue": true }
 },
 "someArray": [1,2,3],
 "subComponentList": [
 {
 "component": "ListItem",
 "id": "ListItem-444",
 "data": { "title": "444s title“ }
 },
 {
 "component": "ListItem",
 "id": "ListItem-555",
 "data": { "title": "555s title“ }
 }
 ]
 }
 }
  21. {
 "ParentComponent-1234": {
 "component": "ParentComponent.jsx",
 "id": "ParentComponent-1234",
 "data": {
 "key":

    "value",
 "other": "data",
 "number": 42,
 "subComponent": "SubComponent",
 "someArray": [1, 2, 3],
 "subComponentList": [ "ListItem-444", "ListItem-555"]
 }
 },
 "SubComponent": {
 "component": "SubComponent.jsx",
 "id": "SubComponent",
 "data": { "boolValue": true }
 },
 "ListItem-444": {
 "component": "ListItem",
 "id": "ListItem-444",
 "data": { "title": "444s title“ }
 },
 "ListItem-555": {
 "component": "ListItem",
 "id": "ListItem-555",
 "data": { "title": "555s title" }
 }
 }
  22. var React = require('react');
 var SubComponent = require('SubComponent');
 var ListItem

    = require('ListItem');
 
 module.exports = React.createClass(
 {
 displayName: 'ParentComponent',
 render: function () {
 var items = [];
 
 for (var key in this.props.subComponentList) {
 if (this.props.subComponentList.hasOwnProperty(key)) {
 items.push(
 <li>
 <ListItem {...this.props.subComponentList[key]}></ListItem>
 </li>
 );
 }
 }
 
 return (
 <div className="{this.props.key}">
 <h1>{this.props.other} Hello PHP {this.props.number}</h1>
 <SubComponent {...this.props.subComponent}></SubComponent>
 <ul>{items}</ul>
 </div>
 );
 }
 }
 );
  23. var React = require('react');
 
 module.exports = React.createClass(
 {
 displayName:

    'ListItem',
 render: function () {
 return (
 <div>
 {this.props.title}
 </div>
 );
 }
 }
 );
  24. SEO

  25. Profile Publications Publication Publication Publication AboutMe LeftColumn Image Menu <div

    id="placeholder"></div> <script>loadWidget('/aboutMe', function(w) { w.render({ replace : '#placeholder' }); })</script> Institution