I’ve been playing around for a while with Facebook’s answer to multi-platform real-time apps, React.js and React Native.
I can say I’m thoroughly convinced million-user architectures will default to React, JSX, and a flavor of Web Sockets or WebRTC for handling real-time.
Today I’m going to share with you a nice surprise I got when building a test app with React.js and Semantic UI.
It’s hard to convey how much of a pain in the butt it can be to use pre-existing Javascript frameworks and libraries along with React.js, simply because of how jelous React is about any and all DOM manipulations.
If you want to successfully build React applications which have a nice look and feel you’re going to have to resign to the fact that jQuery drop-downs/accordions/etc, popular fancy shmancy sliders, and any Javascript-based eye candy module will be incompatible with React right out of the box. I’ll repeat, from now on React is master of your app and no one else is allowed DOM access. So if you want a fancy calendar, either build it yourself or better yet turn to the React community which is actually incredibly active and have already shared very viable replacements for most popular UI modules.
With this said, if you’ve ever used Semantic UI, you might be wondering then how I can sit here and write an article about how it fits in with React.js when many of its components rely on Javascript DOM manipulations.
And here-in lies the positive surprising conclusion I arrived to while experimenting…
Semantic UI is built mainly on CSS, so Javascript is optional
That’s right. All those modals, drop-downs, and what-not, all of them work based off of css classes. Even animations, although we’ll get to those later on.
The little Javascript used by Semantic is actually just there to turn add and remove classes, making boosts overall performance and actually provides the perfect gateway with which to plug React with this fantastically simple framework.
I’ll stick to the basics in this post and will be sure to build a working app in the near future.
Using a Semantic UI modal in a React.js app
Let’s say you have an app which displays a button that when clicked displays a modal. On out-dated basic HTML your whole app would look like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Boring Old Modal</title>
<link rel="stylesheet" href="semantic.min.css">
<script src="jquery.min.js"></script>
<script src="semantic.min.js"></script>
<script>
$(function(){
$('.button').click(function(){
$('.ui.modal').modal('show');
});
});
</script>
</head>
<body>
<div class="ui teal button">Show Modal</div>
<div class="ui small modal">
<div class="ui center aligned header">Hello</div>
<div class="content">
<p>World</p>
</div>
</div>
</body>
</html>
Now, how about we spruce things up a little? Let me show you the React way of handling this:
var HelloModal = React.createClass({
getInitialState: function() {
return {
visible: false
};
},
componentDidMount: function() {
var self = this;
$(window).on('modal.visible', function(ev){
self.setState({visible: true});
});
$(window).on('modal.hidden', function(ev){
self.setState(self.getInitialState());
});
},
render: function() {
var modal_classes = (this.state.visible)? 'ui small modal transition visible active' : 'ui small modal transition hidden';
return (
<div className={modal_classes}>
<div className="ui center aligned header">Hello</div>
<div className="content">
<p>World</p>
</div>
</div>
);
}
});
var ModalPage = React.createClass({
getInitialState: function() {
return {
visible: false
};
},
componentDidMount: function() {
var self = this;
$(window).on('modal.visible', function(ev){
self.setState({visible: true});
});
$(window).on('modal.hidden', function(ev){
self.setState(self.getInitialState());
});
},
handleClick: function(ev){
if (ev.target == this.getDOMNode()){
$(window).trigger('modal.hidden');
}
},
render: function() {
var modal_classes = (this.state.visible)? 'ui dimmer modals page transition visible active' : 'ui dimmer modals page transition hidden';
return (
<div className={modal_classes} onClick={this.handleClick}>
<HelloModal />
</div>
);
}
});
var showModal = function(){
$(window).trigger('modal.visible');
}
$(function(){
React.render(
<div>
<div className="ui teal button" onClick={showModal}>Show Modal</div>
<ModalPage />
</div>,
document.getElementById('app')
);
});
Ok, that might seem like a bit much, but you’ve got to remember React is not meant for prototyping hello world apps, what might seem like overkill for a single modal, becomes extremely useful when handling thousands of them in real-time across multiple instances of the app.
In any case, here’s the brake-down; our React app has two components:
The modal, which is basically a copy and paste within a React class of the good ol’ HTML we had before.
var HelloModal = React.createClass({
// ...
render: function() {
var modal_classes = (this.state.visible)? 'ui small modal transition visible active' : 'ui small modal transition hidden';
return (
<div className={modal_classes}>
<div className="ui center aligned header">Hello</div>
<div className="content">
<p>World</p>
</div>
</div>
);
}
});
And the modal container; this one would generally be created by Semantic UI when calling $(x).modal();
, and again, React owns the DOM, so:
var ModalPage = React.createClass({
// ...
render: function() {
var modal_classes = (this.state.visible)? 'ui dimmer modals page transition visible active' : 'ui dimmer modals page transition hidden';
return (
<div className={modal_classes} onClick={this.handleClick}>
<HelloModal />
</div>
);
}
});
In both cases the modal_classes
variable will be how we’ll control the modal showing and hiding (in semantic CSS classes the transition visible active
and transition visible hidden
respectively).
To manage the communication between React components effectively we can leverage Semantic’s dependency on jQuery, using jQuery to dispatch events to update their states.
Both for the modal and the modal container we make sure to initialize their respective states so that it tracks the visibility of the component:
// ...
getInitialState: function() {
return {
visible: false
};
},
// ...
Then, once the component has been mounted we attach the event listener which will show and hide it:
// ...
componentDidMount: function() {
var self = this;
$(window).on('modal.visible', function(ev){
self.setState({visible: true});
});
$(window).on('modal.hidden', function(ev){
self.setState(self.getInitialState());
});
},
// ...
On the modal container we can manage the hiding of it all when clicked by generating a click handler:
// ...
handleClick: function(ev){
if (ev.target == this.getDOMNode()){
$(window).trigger('modal.hidden');
}
},
// ...
and then attaching it:
// ...
<div className={modal_classes} onClick={this.handleClick}>
// ...
And that’s it!
The HTML needs to load our JSX and wil become just a scaffold which ends up looking like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>React Modal</title>
<link rel="stylesheet" href="semantic.min.css">
<script src="jquery.min.js"></script>
<script src="semantic.min.js"></script>
<script src="babel-core.browser.js"></script>
<script src="react.js"></script>
<script type="text/babel" src="ReactApp.jsx"></script>
</head>
<body>
<div id="app">
<!-- template content goes here -->
</div>
</body>
</html>
As you can see, from the script dependencies, we can use babel toautomagically transform JSX to runnable code simply by setting the type
of the .jsx
file to text/babel
.
Here’s a Gist I uploaded with working code for you to try out:
https://gist.github.com/jeanlescure/b50d2393036f4dbdc183
And here’s a comparison between the old HTML and the React.js version:
As I mentioned earlier, animations on Semantic UI are also handled on CSS, but, I’ve noticed that the way it does it is by adding animation CSS classes to it’s animated modules and then removing them after completion. So maybe a React animation shim for Semantic might be needed.
In any case, for all intents and pruposes, React and Semantic play along way better than I could’ve anticipated, so expect a follow-up article with an actual working app.
Happy Coding!