Modernizing SOL

with React

Eirik Vullum

eiriklv

http://vullum.io

2016

Big shift

4

13

> git push

ReactJS Oslo Meetup

Curation

Aggregation

Live News

Content

Personalized

Content

Pain points

Shoehorning

Optimize for change

Implicit coupling

Isolated building blocks

Fragmentation

Single platform

Integrating Ads

Requirements

Assemble dynamically

Fast page loads

Any backend

A/B

Preview

Routing

<Router history={browserHistory}>
  <Route path="/" component={App}>
    <Route path="about" component={About}/>
    <Route path="users" component={Users}>
      <Route path="/user/:userId" component={User}/>
    </Route>
    <Route path="*" component={NoMatch}/>
  </Route>
</Router>
<Router history={browserHistory}>
  






</Router>

?

User / browser

Front end app

API

PATH: /sport

PATH: /routes/sport

Config for /sport

Current /sport page

{
  root: {
    component: 'sol-frontpage-app',
    props: {
      children: [{
        component: 'sol-header',
        props: { ... }
      }, {
        component: 'sol-article-preview',
        props: { ... }
      },
      ...
      {
        component: 'sol-footer',
        props: { ... }
      }]
    }
  }
}
<ArticlePreview
  title='....'
  url='......'
  image='....'
/>
<GoogleAd
  name='top-banner'
/>
getConfig(route)
getConfig(route)
.then(mapToComponents)
{
  root: {
    component: 'sol-frontpage-app',
    props: {
      children: [{
        component: 'sol-header',
        props: { ... }
      }, {
        component: 'sol-article-preview',
        props: { ... }
      },
      ...
      {
        component: 'sol-footer',
        props: { ... }
      }]
    }
  }
}
{
  root: {
    component: <FrontpageApp>,
    props: {
      children: [{
        component: <Header>,
        props: { ... }
      }, {
        component: <ArticlePreview>,
        props: { ... }
      },
      ...
      {
        component: <Footer>,
        props: { ... }
      }]
    }
  }
}
getConfig(route)
.then(mapToComponents)
getConfig(route)
.then(mapToComponents)
.then(render)
function render(config) {








}
function render(config) {
  const Root = config.root.component;







}
function render(config) {
  const Root = config.root.component;

  const props = config.root.props;





}
function render(config) {
  const Root = config.root.component;

  const props = config.root.props;

  ReactDOM.render(
    <Root {...props} />,
    document.getElementById('#root')
  );
}

Component data

const withData = React.createClass({
  statics: {
    getData(options) {
      ...
    }
  },
  ...
});

Component styling

const withStyles = React.createClass({
  statics: {
    getStyles() {
       return serverStyles;
     },
  },
  ...
});
const withStyles = React.createClass({
  componentWillMount() {
    if (isBrowser) {
      injectStyles(clientStyles);
    }
  },
  ...
});

Asynchronous chunking

'sol-article-preview': (callback) => {
  require.ensure([], (require) => {
    callback(null, require('./ArticlePreview.jsx'));
  });
},

Ads

const withScripts = React.createClass({
  componentWillMount() {
    if (isBrowser) {
      injectScripts();
    }
  },
  ...
});
function injectScripts() {
  (function(w, d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//url-to-script-here.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(window, document, 'script', 'script-id'));
}

Demo

Lessons learned

  • It actually works!
  • Faster development cycle
  • Self contained components are awesome
  • ...But hard to achieve
  • Component data declaration is hard

Thanks!

@eiriklv