Skip to content

Instantly share code, notes, and snippets.

@alicoding
Last active March 15, 2016 18:36
Show Gist options
  • Save alicoding/5b6dfc1d2f41b50a97f1 to your computer and use it in GitHub Desktop.
Save alicoding/5b6dfc1d2f41b50a97f1 to your computer and use it in GitHub Desktop.
react-uograde.MD

Upgrade React to 0.14, React Router to 2.0 and React Intl to 2.0

  1. What did I upgrade?
  2. Why the upgrade?
  3. Why all 3 at once?
  4. How much time did it take to upgrade?
  5. How was all the documentations?

Noticeable/Breaking changes to React 0.14.x from .0.13.x

var React = require('react');

React.render(<Component />, document.querySelector("#my-app"));
var React = require('react');
var render = require('react-dom').render;

render(<Component />, document.querySelector("#my-app"));

Testing with React 0.14.x

  1. unmountComponentAtNode is now in React-dom instead of React object.
  2. getDOMNode is deprecated and has been replaced with ReactDOM.findDOMNode().
var React = require('react');
var ReactDOM = require('react-dom');
var TestUtils = require('react/lib/ReactTestUtils');
var should = require('should');
var App = require('./path/to/your/app.jsx');

var Document = TestUtils.renderIntoDocument(<App />);
var testElement = ReactDOM.findDOMNode(Document);

testElement.value = "something now";
TestUtils.Simulate.change(testElement);
should(...)).equal(...);

React-router 2.0.x

Upgrade from 0.13.x to 2.0.x (Why the hell would ya do that, right?)

Bunch of breaking changes!

Router.run() --> Router.match()
var routes = (
	<Route path="/" handler={require('your/root/app.jsx'}>
		<Route path="name" handler={require('some/component.jsx')/>
	</Route>
);

Now

import { Router, Route, browserHistory } from 'react-router';

  var routes = (<Router history={browserHistory}>
    	<Route path="/" component={require('your/root/app.jsx'}>
		<Route path="name" component={require('some/component.jsx')/>
	</Route>
  </Router>);
var React = require('react');
var Navigation = require('react-router').Navigation;

var routes = React.createClass({
  mixins: [Navigation],
  componentDidMount: function() {
    this.transitionTo('/somewhere');
  },
  render: function() {
    return (
      null;
    );
  }

});
var React = require('react');

var routes = React.createClass({
 contextTypes: {
    router: React.PropTypes.object
  },
  componentDidMount: function() {
	this.context.router.push('/somewhere');
  },
  render: function() {
    return (
      null;
    );
  }

});

React-Router with the server

Background Info:

Donate.mozilla.org currently using Webpack plugin to run through all pages and generate each static(index.html) files for each pages and each locales which can take up to 30 seconds to compile.

Webpack plugin:

var async = require('async');
var routeFileContent = require('./routefilecontent.js');

function SimpleHtmlPrecompiler(paths) {
  this.paths = paths;
}

SimpleHtmlPrecompiler.prototype.apply = function(compiler) {
  var self = this;

  compiler.plugin('afteremit', function(compilation, done) {
    async.map(self.paths, routeFileContent, function(error, results) {
      if (error) {
        compilation.errors.push(error);
      }
      done();
    });
  });
};

module.exports = SimpleHtmlPrecompiler;
import React from 'react';
import Router from 'react-router';
import routes from '../components/routes.jsx';
var Path = require('path');
var FS = require("q-io/fs");

module.exports = function(outputPath, callback) {
  Router.run(routes, outputPath, function(Handler) {
    var index = React.createFactory(require('../pages/index.jsx'));
    var page = React.createFactory(Handler);

    FS.makeTree(Path.join(__dirname, '..', 'public', outputPath)).then(function() {
      var contentOfTheFile = React.renderToStaticMarkup(index({
        markup: React.renderToString(page(values))
      }));

      var nameOfTheFile = Path.join(__dirname, '..', 'public', outputPath, 'index.html');

      FS.write(nameOfTheFile, contentOfTheFile).then(function() {
        callback(undefined, nameOfTheFile);
      }).catch(function(err) {
        callback(err);
      });
    }).catch(function(e) {
      console.log(e);
    });
  });
};
import React from 'react';
import ReactDOM, { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from '../components/routes.jsx';

function render(renderProps) {
  var Index = React.createFactory(require('../pages/index.jsx'));

  function createElement(Component, props) {
    // make sure you pass all the props in!
    return <Component {...props} {...values} />;
  }

  var props = {
   ...
  };

  return ReactDOM.renderToString(<Index>{renderToString(<RouterContext createElement={createElement} {...renderProps} />)}</Index>);
}
module.exports = (request, response) => {
  function replyContent(redirect, content) {
    let query = '';
    if (request.url.search) {
      query = request.url.search;
    }
    response.send(content).type('text/html; charset=utf-8').vary('User-Agent');
  }
  match({ routes, location: request.url.pathname }, (error, redirectLocation, renderProps) => {
    if (renderProps) {
      replyContent(null, render(renderProps));
    }
    // 404 or whatever you want to do here?
    }
  });
};

You don't have to stub routerContext anymore \o/**

var stubContext = require('react-test-context');

 var TestInput = stubContext(Item, IntlContext);
var Page = React.createElement(stubContext(TestInput, IntlContext),{
  name: "test"
});
var Document = TestUtils.renderIntoDocument(Page);

Now:

var Document = TestUtils.renderIntoDocument(<IntlProvider {...IntlContext} ><Item name="test"/></IntlProvider>);

React-Intl from 0.13.x to 2.0.x

var IntlObj = { messages: {...} locale="en" }
React.render(<App {...IntlObj} />, document.querySelector("#my-app"));
var IntlObj = { messages: {...} locale="en" }
render(
    <IntlProvider key="intl" {...IntlObj}>
      <Router createElement={createElement} {...renderProps} />
    </IntlProvider>, document.getElementById(`my-app`)
  );
var React = require('react');

var route-file-content = React.createClass({
  mixins: [require('react-intl').IntlMixin],
  componentDidMount: function() {
    console.log(this.getIntlMessage("yourMSGID"));
    console.log(this.props.locale); // You always have to pass `locale` around in the props
  },
  render: function() {
    return (
      null
    );
  }

});

var React = require('react');

var route-file-content = React.createClass({
  contextTypes: {
    intl: React.PropTypes.object
  },
  componentDidMount: function() {
    console.log(this.context.intl.formatMessage({id: 'yourMSGID'}));
    console.log(this.context.intl.locale); // Now you can simply do this everywhere
  },
  render: function() {
    return (
      null
    );
  }

});

Locale-data is now not included for react-intl 2.0.x for client-side

In your index.html you should include

<script src={`/intl/data/${this.context.intl.locale}.js`}></script>

Then you do this before rendering your component

import {addLocaleData} from 'react-intl';

  let locale = Object.keys(window.ReactIntlLocaleData)[0];
  addLocaleData(window.ReactIntlLocaleData[locale]);
    render(
      <IntlProvider key="intl" {...IntlObj}>
        <Router createElement={createElement} {...renderProps} />
      </IntlProvider>, document.getElementById(`my-app`)
    );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment