Render React components on the server with PHP

Earlier today, I stumbled upon a tweet promoting Facebook's very own React rendering library, based on PHP's v8js, already more than a year and a half of age and surprisingly unpopular - although its poor architecture and flexibility may be a good start in a quest for reasons.

It indeed is designed to spit out, separately, the markup rendered from your root component, and the JavaScript code required to bootstrap the application clientside. The fact it writes the latter itself requires React to be declared in the global scope; however, the major PITA is the fact it needs to be fed React's source and your own separately. Both of these make it utterly incompatible with modern JS build tools and their use of module systems - in my case, native ES6 modules transpiled by Babelify.

I thus decided to bypass it and see how difficult it would be to mimic its behaviour, but in a much simpler way, by simply feeding V8js my bundled, plain-old-ES5 code, with its own bootstrap code using the module syntax - the clientside code, actually.

It turned out great! A quick hack on the entry point:

import React from 'react';
import MyComponent from './components/my';

if ('undefined' !== typeof document) {
React.render(<MyComponent />, document.body);
} else {
print(React.renderToString(<MyComponent />));
}

And you're good to go:

$v8js = new V8Js();
ob_start();
$v8js->executeString(file_get_contents('/path/to/app.js'));
$content = ob_get_clean();

echo($content); in the client page then displays your gorgeous React app, rendered on the server.

Have fun!

Deploy to your own server through SSH with Travis CI

Have small projects on the continuous integration service Travis CI and want to deploy them to your own server through SSH, but cannot stand the hassle of setting up an advanced tool like Capistrano? You have come to the right place! In this post, I will show you how to deploy a Symfony project with some Gulp-powered asset management, but the following "technique" should suit any technology stack you want to use just fine.

We want our production server to handle as few roles as possible, apart from serving our PHP and static content to our end users, which goes without saying - we absolutely do not want it to compile assets, for example. Travis will therefore do everything for us, and we will simply package the result and send it to our server, where we will also trigger a simple shell script to take care of the final adjustments.

For the sake of simplicity, we will authenticate against our server with a plain old username/password pair. We would obviously be better off using a SSH key. We will also assume your project lives at /home/project/www on this server, and Composer is installed there at /usr/local/bin/composer.

First things first, here is our initial.travis.yml file:

language: php
sudo: false

before_script:
- composer install --prefer-source
- npm install

script:
- phpunit
- gulp --production

Let's append an after_success section to it, which will remove development dependencies and package our build:

after_success:
- export SYMFONY_ENV=prod && composer install --no-dev -o
- mkdir build
- mv * build
- tar -czf package.tgz build

Note: to exclude any production-irrelevant file or folder from the package, use the --exclude option.

It is time to send this package to its destination through scp. But wait! This file will eventually get committed to our repository, and we most certainly do not want to expose our production server's credentials for the world to see... So, unless our repository is private (which would not make it less of a bad idea), we want these informations to be encrypted in some way. Travis offers just that in the form of a command-line utility:

sudo gem install travis
cd /path/to/our/repository
travis encrypt DEPLOY_HOST=123.45.67.89
travis encrypt DEPLOY_PATH=/home/project
travis encrypt DEPLOY_USER=randy
travis encrypt DEPLOY_PASS=marshM4LL0W

Those last four commands will output some YAML with secure as the key, and our encrypted variable as the value. Paste these in a new env section in your .travis.yml:

env:
global:
- secure: "..."
- secure: "..."
- secure: "..."
- secure: "..."

We are now able to reference these variables in our build! To ease things a little, we will make use of sshpass, which allows us to authenticate through SSH with a password in a one-line, non-interactive way. Tell Travis to install it:

addons:
apt:
packages:
- sshpass

We will now make use of the tool's -e option, which will read the password from the eponymous environment variable:

after_success:
- export SYMFONY_ENV=prod && composer install --no-dev -o
- mkdir build
- mv * build
- tar -czf package.tgz build
- export SSHPASS=$DEPLOY_PASS
- sshpass -e scp package.tgz $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH

Note: you can append -o stricthostkeychecking=no to the scp call to bypass host key verification if necessary.

Now that Travis can copy our package to the server, we just need to set up our deployment script in /home/project/deploy.sh:

#!/bin/bash
cd `dirname $0`

# Extract the package
tar -xzf package.tgz
rm package.tgz

# Copy any file we want to keep from build to build
cp www/app/config/parameters.yml build/app/config/parameters.yml

# Swap it all around, keeping the previous version aside in case something goes wrong
rm -rf www_old
mv www www_old
mv build www

We also tell Travis to run it as its final step:

after_success:
- export SYMFONY_ENV=prod && composer install --no-dev -o
- mkdir build
- mv * build
- tar -czf package.tgz build
- export SSHPASS=$DEPLOY_PASS
- sshpass -e scp package.tgz $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH
- sshpass -e ssh $DEPLOY_USER@$DEPLOY_HOST $DEPLOY_PATH/deploy.sh

And here we are! Every successful Travis build will now trigger a production deployment.

Note: it is strongly advised to restrict this to direct pushes on the master branch by prefixing all after_success steps with test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && .

Handle React's contenteditable warning in Jest

In complement to , here is a quick note regarding the warning thrown by React when using contenteditable on children elements of a component.

Although it does not seem to be going anywhere, you can silence it in a somewhat hacky way in Jest by doing the following :

console.warn = jest.genMockFunction();
someCallTriggeringTheWarningANumberOfTimes();
expect(console.warn.mock.calls.length).toBeGreaterThan(0);

Here is the issue I opened on GitHub on the subject, where I have been told some work would be put into making this particular warning less of a pain in the testsuite. Go on and leave a comment there if it annoys you as well !

Mock your React components with Jest

Unit testing of React components has been made possible by the folks over at Facebook in the form of Jest, a JavaScript test framework built on top of Jasmine. Despite a lack of maturity on some aspects at the current time, the tool proves itself useful overall. This article is meant to help you (and future me) avoid some pitfalls I encountered when getting started with it, especially regarding the use of mocks.

First things first, let's install Jest :

npm install -g jest-cli
npm install react-tools --save-dev

You will then need to configure the tool to suit your setup, the easiest way to do that being to amend the package.json file directly :

"jest": {
"rootDir": "relative/path/to",
"scriptPreprocessor": "<rootDir>/preprocessor.js",
"testDirectoryName": "tests",
"unmockedModulePathPatterns": ["<rootDir>/../../../../node_modules/react"]
},

Your test files will, according to this example, have to be located in a relative/path/to/tests folder. At the same level as this folder, you will have to set up a preprocessor.js file such as the following :

(function () {
'use strict';

var ReactTools = require('react-tools');

module.exports = {
process: function(src) {
return ReactTools.transform(src);
}
};
})();

As you can see, we make use of the react-tools package we installed beforehand, which is meant to compile your JSX files in a standalone way in order to feed them to Jest (and, underneath, Jasmine).

The last line of the framework's configuration tells it not to mock React itself, as it automatically mocks pretty much everything and lets you decide what you want to preserve by using jest.dontMock('something') in your tests.

As for it, my personal recommandation would be to turn automocking off in the first place, as it has brought me more trouble than benefits. You can do that by using jest.autoMockOff(), and use the reverse logic to mock just what you need with jest.mock('something'). Actually, I have not been using that a lot, preferring to mock single methods here and there by replacing them with someModule.someMethod = jest.genMockFunction().

Speaking of mocks, you should know there currently is an open issue about the fact you cannot spy on stubbed method calls when these calls are meant to happen in response to an user event, such as onClick and the likes.

One great thing about tests is the way they make you think twice about your code. When using Jest, you will soon realize you want to avoid putting logic directly in methods such as componentWillMount, since they cannot be stubbed before the component's mounting into the DOM. Instead, put this logic in a separate function that will be called both by componentWillMount and manually in your test, so you can stub things and toy around with your component's data before updating its rendering.

If you need to mock a method that returns a promise and want to avoid using a dedicated library for that, here is a quick'n'dirty way to achieve just that :

jest.genMockFunction().mockReturnValue({
then: function(callback) {
callback(someArrayOrObjectOrWhateverThePromiseYields);
}
});

That will be all for now. Other small articles on the same topic may follow soon, stay tuned !

At the time of the writing of this article, I also had an issue where Jest would crash randomly, apparently because of some permission issue related to the .haste_cache folder. I tried running it with sudo, which not only worked but was not necessary anymore after the single time I used it.

Render a React component with attributes defined in the DOM

Using the JavaScript UI component library React is, among other benefits, a good way to disseminate small pieces of SPA intelligence into a more classical website, although it can sometimes be tricky to conciliate both worlds.

For this example, we will be building a form, one field of which holds a color value. We already set up a classic <input> to allow our users to type in the hexadecimal code they desire ; however, we would like to improve their experience by adding a colorpicker control that will change the field's value for them in a more pleasant way.

Having already been using React here and there throughout the project, we chose a library that makes use of it, and provides what we need as a React component. Since we want it to control the field, the latter needs to be rendered by React as well, which means we will roll out a component of our own that includes both.

We quickly face a dilemma, though : we still want the <input>'s value to be POSTed to the server under the right name, and we don't want to delegate to React the handling of CSS classes and such. All of the element's attributes shall keep being defined in our HTML template, for the sake of separation of concerns. Unfortunately, React doesn't allow us to fetch such data from the DOM when rendering its components out of the box : this is the issue we are going to address here.

First, let's define a target element for our component to render into (nothing new here) :

<div class="color-picker"></div>

We will now append every attribute we want applied to our <input> to this <div>, prepending the keys with data- to keep everything safe and valid :

<div class="color-picker" data-class="some-class" data-name="color" data-value="#BADA55"></div>

Now, we want the <input> rendered by React to get all these attributes applied to it (without the data- prefix, obviously), so everything not related to React keeps working just like without it. Here is our component's code :

var React = require('react'),
ReactColorPicker = require('react-color-picker');

module.exports = React.createClass({
getInitialState: function() {
return {
color: '#FFFFFF',
attrs: {}
};
},

updateColor: function(color) {
this.setState({ color: color.toUpperCase() });
},

// Called by React right after mounting the component in the DOM
componentDidMount: function() {
var parentNode = this.getDOMNode().parentNode,
attrs = this.state.attrs;

// We iterate over the target node (our div)'s attributes
[].slice.call(parentNode.attributes).forEach(function (attr) {
if (attr.name.match(/^data-/)) {
var realName = attr.name.substr(5);

if ('value' == realName) {
// The value will be handled by React directly, as a separate state variable
this.updateColor(attr.value);
} else {
// This follows React's convention, class being a JS keyword
if ('class' == realName) {
realName = 'className';
}

// Other attributes will be grouped in an object
attrs[realName] = attr.value;
}

// Clean up the target node
parentNode.removeAttribute(attr.name);
}
}, this);

// We trigger our component's update to make it rerender with its new attributes
this.setState({ attrs: attrs });
},

render: function() {
// The {...var} notation allows us to apply a key-value JS object
// as a collection of HTML attributes
return (
<div>
<ReactColorPicker value={this.state.color} onDrag={this.updateColor} />
<input value={this.state.color} {...this.state.attrs} onChange={this.updateColor} />
</div>
);
}
});

Hopefully, future versions of the tool may allow us to render components dynamically based on the DOM's initial state more easily. A cleaner solution could have been to handle our field with a subcomponent and make these attributes part of its props, to enhance its reusability ; it would also be nice to display the <input> natively inside the <div>, so it remains available without JavaScript, but this might lead to some markup duplication - as for the attributes themselves, it should not be a problem if your form is part of a server-side template.

I will be glad to hear what you have to say about this, if you ever encountered similar situations and if you have a better solution to handle such cases : don't be shy !