React.js: how to decouple jsx out of JavaScript
JavascriptReactjsReact JsxJavascript Problem Overview
Is there any way to move the jsx from a component's render function to a separate file? If so, how do I reference the jsx in the render function?
Javascript Solutions
Solution 1 - Javascript
You can use react-templates. It gives you exactly this separation between the markup and the component itself, and much more.
I found it very useful for my needs (a large scale web app).
Solution 2 - Javascript
One problem with moving templates into a separate file is that if you use handlers like:
var myTemplate = (
<form onSubmit={this.handleSubmit}></form>
);
and then in your component you use:
render: function() {
return myTemplate;
}
the generated template code will call this.handleSubmit(), so the "this" will be wrong and the handlers won't work. What you need to do is put them in a function, like this:
var myTemplate = function() {
return (
<form onSubmit={this.handleSubmit}></form>
);
};
then in your component's render function, you need to bind it to 'this' correctly, then call it, like this:
render: function() {
return myTemplate.bind(this)();
},
Now you can put that template definition anywhere, in a separate file or however you want to structure and reference your own code. (power to you! Don't listen to these crazy prescriptive frameworks! :) )
Solution 3 - Javascript
Here is a pattern for separating the template jsx that uses CommonJS modules in NodeJS, Browserify or Webpack. In NodeJS, I found the node-jsx module helpful to avoid the need to compile the JSX.
// index.js
require('node-jsx').install({extension: '.jsx'});
var React = require('react'),
Component = require('./your-component');
// your-component.jsx
var YourComponent,
React = require('react'),
template = require('./templates/your-component.jsx');
module.exports = YourComponent = React.createClass({
render: function() {
return template.call(this);
}
});
// templates/your-component.jsx
/** @jsx React.DOM */
var React = require('react');
module.exports = function() {
return (
<div>
Your template content.
</div>
);
};
Update 2015-1-30: incorporated suggestion in Damon Smith's answer to set this
in the template function to the React component.
Update 12/2016: the current best practice is to use the .js extension and use a build tool like Babel to output the final javascript from your source. Take a look at create-react-app if you're just getting started. Also, the latest React best practices do recommend a separation between components that manage state (typically called "container components") and components that are presentational. These presentational components can now be written as functions, so they are not far off from the template function used in the previous example. Here is how I would recommend decoupling most of the presentational JSX code now. These examples still use the ES5 React.createClass()
syntax.
// index.js
var React = require('react'),
ReactDOM = require('react-dom'),
YourComponent = require('./your-component');
ReactDOM.render(
React.createElement(YourComponent, {}, null),
document.getElementById('root')
);
// your-component.js
var React = require('react'),
YourComponentTemplate = require('./templates/your-component');
var YourComponentContainer = React.createClass({
getInitialState: function() {
return {
color: 'green'
};
},
toggleColor: function() {
this.setState({
color: this.state.color === 'green' ? 'blue' : 'green'
});
},
render: function() {
var componentProps = {
color: this.state.color,
onClick: this.toggleColor
};
return <YourComponentTemplate {...componentProps} />;
}
});
module.exports = YourComponentContainer;
// templates/your-component.js
var React = require('react');
module.exports = function YourComponentTemplate(props) {
return (
<div style={{color: props.color}} onClick={props.onClick}>
Your template content.
</div>
);
};
Solution 4 - Javascript
I just separated JSX into anonymous function files
template.js
export default (component) => {
return <h1>Hello {component.props.name}</h1>
}
my-component.js
import React, {Component} from 'react';
import template from './template';
export default MyComponent extends Component {
render() {
return template(this);
}
}
In template you can access props or state or functions using component
variable.
Solution 5 - Javascript
If you don't use any module system, i.e. rely on script
tags only, simple expose your JSX component in a global variable and use it when you need :
// component.js
var Component = React.createClass({ /* your component */ });
// main.js
React.renderComponent(Component({}), domNode);
Note : the script
tag for component.js must appear before the script
tag for main.js.
If you use a Commonjs-like module system like Browserify, simply export your component definition and require it when you need it.
// component.js
var React = require("react");
module.exports = React.createClass({ /* your component */ });
// main.js
var Component = require("component.js");
React.renderComponent(Component({}), domNode);