Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

 

Cutting through the noise: Why SilverStripe 4 will use ReactJS

We are incorporating ReactJS into SilverStripe 4. In this article, we’ll discuss how we arrived at this decision, and why we think React is deserving of all the attention it’s getting.

Read post

At SilverStripe, we’ve always aimed to reserve judgement on new web technologies until they have sufficiently proven themselves viable. Most of the time, this has been a worthy strategy. Sometimes, we’re behind the curve. But more often than not, we find that we don’t miss much, and we’re happy to keep our APIs predictable, using tools that most developers understand or can use easily.

In the upcoming release of SilverStripe 4, we’ve talked about welcoming acclaimed CSS framework Bootstrap into our codebase. This is entirely consistent with that pattern. At the time of our last major release, Bootstrap was still fairly new, but four years later, it’s hard to deny its viability. However, we also announced the incorporation of ReactJS – a library that is fairly new, and has yet to reach a stable release. So what is it about React that makes us so bullish?

In this article, we’ll discuss how we arrived at this decision, why we think React is deserving of all the attention it’s getting, and what it means for SilverStripe.

The writing is on the web

First, let’s get the easy stuff out of the way. To find out if ReactJS, even in its pre-release state, is a viable technology, just survey the landscape. It’s currently a primary toolkit for some of the web’s heaviest hitters, including:

Additionally significant is that the library is the brainchild of Facebook, and the company continues to integrate more and more React into the codebase of the world’s #1 web application. You can’t get better dogfooding than that. By comparison, while Google created and maintains AngularJS, they do not consume it to any reasonable degree in production.

So when it comes to the question of viability, it simply isn’t a matter of opinion. The evidence is out there, and it tells a very compelling story.

We’ve been next-best-thinged to death.

The Javascript world is crazy. It really is. Every time you check your Twitter feed, there’s some new thing that you’ve never heard of that is already being deployed to a million production sites and heralded as the next big thing by all your favourite blogs. You get back from lunch, check again, and that thing has been eclipsed by some other thing. If you’re a Javascript enthusiast, this is real life, and it’s just par for the course. When you sold out on Grunt just to trade it in a month later for Gulp, you probably didn’t even think it was unusual. This is the world we live in.

So why should you care about yet another next-best-thing like React?

There are a number of important differentiators we should consider that separate React from everything else at the garage sale of free, open-source Javascript things that will change the world.

For one, React isn’t another Javascript framework. It’s a UI library. When you hear the term “UI library,” you probably think of things like jQuery-ui and Bootstrap, but React is a lot more focused than that. It offers nothing in terms of visual design. It’s a library because it does one thing well, and that one thing is rendering UI components. It is largely unopinionated, and it doesn’t care about what tools you employ beyond its scope of rendering UI.

Dishwashers have terrible APIs

I’ve come to discover having owned one for many years, that the name "dishwasher" is a false promise. In order to get reliably clean dishes, you really have to know a lot about how a dishwasher works. You have to angle the dishes correctly, understand where the water is coming from, and know what should go on top and what should go underneath. In short, this is a terrible API. You shouldn’t have to know anything about the inner workings of an appliance. It should do what you ask it to do in the simplest way possible.

Over the last decade, we’ve been fed myriad different approaches to writing Javascript. First, jQuery told us that Javascript was about assigning behaviour and mutating the DOM with CSS selectors, plugins, and chainable methods. Still to this day, it’s not uncommon to hear a new developer say something akin to, “I know jQuery, now I want to learn Javascript.” jQuery marked the beginning of a widespread movement to transform Javascript into something it didn’t want to be.

A few years later, we saw Handlebars, which told us that Javascript was a templating language now. With the rise of Handlebars came BackboneJS, a full-stack “MVC” framework for Javascript that introduced new ideas like models and controllers that sought to fundamentally redefine client side programming.

Once we signed off on Backbone, it gave a carte blanche to the chefs in the Javascript Kitchen to cook us anything, because, God knows, we would eat it up. So began the feast – SpineJS, EmberJS, JavascriptMVC, AngularJS, KnockoutJS – it got to a point where you could point to any word in the dictionary, and chances were fairly good that a Javascript MVC framework had claimed it as a whimsical brand.

All of these frameworks are dishwashers. They all ask you to think about Javascript in a different way. They all ask you to babysit them, get down to their level, and understand how they like to do things.

As an example, here’s a snippet of KnockoutJS:

Screenshot 2016 02 22 14.45.24

Look at all the new stuff we’re being exposed to here. First, we have to learn the concept of a “view model,” which is part of Knockout's lexicon. We have to wrap our initial value of 0 with ko.observable in order to tell Knockout that this value will change over time. Your instinct as a developer is to simply write "0", but that isn't what the framework wants from you. Further down, we introduce the concept of pureComputed, which we can only gather is a way of educating Knockout that a value is computed, and the function is pure.

The template isn’t any less esoteric. We get some kind of pseudo-JSON language polluting our markup.

Screenshot 2016 02 22 14.45.52

Ember isn’t much clearer. We have more “computed” properties, in addition to more idiosyncratic noise like “oneWay”, “observes”.

Screenshot 2016 02 22 14.46.47

Here, Ember is casting attributes, and using some kind of DSL with @each:.

Screenshot 2016 02 23 09.57.53
Angular is a beloved Javascript framework, but idiosyncratic in its own right. Here we have an all-you-can-eat buffet of idiosyncrasies: modules, directives, controllers, and watching. These are all new things we have to learn simply because Angular is special.

Screenshot 2016 02 22 14.50.16

So what’s the big deal? Every framework has idiosyncrasies. Laravel has idiosyncrasies. SilverStripe has idiosyncrasies. PHP itself has idiosyncrasies.

Idiosyncrasies are the opposite of features

What makes all of this particularly costly is that idiosyncrasies are the opposite of features. A feature is when software agrees to do what you ask, and it doesn’t ask any questions. For example, OSX has no idea why, when you hold down you mouse button and drag, it should rearrange the pixels to make it look like the object has moved across the screen, but it does it anyway, because you’re the human and that’s what you want.

An idiosyncrasy is the opposite of that. That’s when you, the human, have to acquiesce to what the framework wants from you. You just want to declare a default value of 0, like any reasonable person would, but you can’t do that. You have to get down to the level of the framework, and understand that you must wrap that value with observable(), because that’s what the framework wants. You have to trade common sense for the privilege of being able to communicate with your framework. That is the opposite of a feature.

React is just Javascript

React is a breath of fresh air. It turns its back on this endless stream of new concepts in Javascript, and gets back to basics. Javascript is great, and it should speak for itself.

Let’s look at a React component.

Screenshot 2016 02 22 15.12.54

Everything you need to know about React is in the first two lines of code: props and state. If you know those two things, you’re a React developer. If you know anything more than that, you’re an advanced React developer. React is pure. It’s declarative. It’s deterministic. Without knowing anything about its inner workings, you can understand what it does.

You’re not separating your concerns!

Many developers shift in their seats when they see React code for the first time, because it violates all kinds of best practices that we’ve been taught over the last 10 years. We’re mixing behaviour with our UI. We’re colocating HTML and Javascript. We have onClick attributes. This is bad.

It’s actually not. React makes the case, compellingly, that these are not separate concerns. A component should provide UI and also behaviour. To think of them as separate concerns is misguided.

By contrast, let’s look at a framework that allegedly does separate concerns. Here’s an Angular template:

Screenshot 2016 02 22 15.35.36

What have we done to get to this point? This technology is screaming to be something else. If this is your template, and it was created in the interest of simply separating concerns, you’ve already lost the game. There’s no way this technology will ever be as good as you need it to be. Maybe it’s time to reevaluate the validity of the goal to separate concerns. This template wants to be Javascript, so let it be Javascript!

All of this is nicely captured by a famous quote from core React developer Pete Hunt:

Templates don’t separate concerns. They separate technologies.

React is about components

Components are the building blocks of React. They’re composable, declarative descriptions of your UI. They should do one thing, and their output should be very predictable. Here is a simple React component:

Screenshot 2016 02 22 16.01.25

The first line is an example of how we render the component to the template, using an XML-like syntax. Below that is its render() method which determies its output. The attributes we pass into <NavItem /> are called props, and they’re essentially the public API of your component. They’re tantamount to the named arguments you pass into a function call. The render() method uses those to build some HTML.

Why is there HTML in a Javascript file? There isn’t. The syntax you’re seeing is called JSX, and it is simply syntactic sugar for function calls. <li className={classes} /> compiles down to ReactDOM.createElement('LI', {className: classes}). You’re welcome to express the UI that way if you like, but most developers prefer the cleanliness of JSX.

SilverStripe already offers components

One of the most uncelebrated features of SilverStripe 3.0 was composable includes. Before 3.0, includes were fairly useless. All they did was export some template syntax to a shared location, but beyond that, they were very coupled with their parent templates. If they were designed to be in a <% loop %> block for a specific DataObject, you couldn’t really use them elsewhere.

Today, we have composable includes, which means we can pass in the attributes and values we want in the include. This means they’re more reusable, and less coupled.

Here is the exact same component, rendered in SilverStripe template syntax:

Screenshot 2016 02 22 16.07.28

Now properties like $Link don’t depend on the output of a public computational method on your DataObject. It can be anything you want it to be. This is the beauty of composition, and it’s nothing new.

The web already has components

A great example of UI components that you use all the time are form elements. It’s a wonderful thing that every time you want a dropdown selector on your page, you don’t have to create five <div> tags and write a bunch of Javascript. Rather, you can simply express it as <select> with some attributes (props) and some nested components. HTML5 has introduced several more of these – sliders, range selectors, and more.

There’s no reason why UI elements in the user space should not be components, too. To that end, we already have a wonderful React Bootstrap library that allows you to render all of Bootstrap’s core components in React syntax.

Components manage their own state

Data changing over time is the root of all evil.

Pete Hunt

If you’ve ever worked on an application that manages a lot of state, you know that it’s a quagmire. You’re tracking updates to different DOM nodes coming from all different directions, and you’re just praying that the UI is true.

In server-side programming, this is a really easy problem to solve. You simply capture some state, be it from the database, or an API call, or anything else, plug it into a template, and send it down the wire. That template is just a description of what the application should look like at any given time. When you want new state, you simply refresh the page.

React components are designed exactly the same way. Let’s look at this example:

Screenshot 2016 02 22 16.39.01

We define a series of variables that we’re going to need to create the view. Then, we simply return the UI using all of those calculations. This is akin to assigning properties in a controller to later call them on the template.

But what happens when the state changes? There’s no “refresh” button in a React component.

As it turns out, there is. Every time you change the state in React, it re-renders the component. This is the only reliable way to ensure that your state is always accurate. Just like in the server-side paradigm, the only way to get the most current state is to refresh the browser.  

A natural reaction to this pattern of frequent refreshing is to assume that it’s really slow. It would be, if it weren’t for the Virtual DOM. React maintains a clone of your DOM in memory, and when the state of your app changes, it creates a new one, and compares it to the old one. Based on that, it can diff the changes and figure out the minimal amount of DOM changes it has to make. If you have a list of 10,000 items, and you only deleted one, then that’s only one DOM change. It doesn’t re-render the entire list.

JSX and Virtual DOM are not idiosyncratic.

Earlier in the article, I discussed the drawbacks of Javascript frameworks that are ceaselessly reinventing what the language is designed to do. Now we’ve exposed concepts like JSX and Virtual DOMs. How is that not idiosyncratic?

I would argue that they’re not. As stated earlier, JSX is simply syntactic sugar for function calls. In fact, JSX has been decoupled from React, and is now its own library. If you want to call functions using XML-like syntax, with attributes used for the parameters, you can do that now. It has nothing to do with React.

Virtual DOM is a bit idiosyncratic, but remember, you don’t need to know anything about it. If you had never even heard of the concept of a Virtual DOM, you could still write high-performing React applications. All you have to do is set the state of your component, and it takes care of the rest.

So where to?

At SilverStripe, we’re extremely enthused about React and where it’s headed. We’ll be merging it into the core to ship with SilverStripe 4. As of now, the one section of the CMS we know will use it is AssetAdmin (the Files & Images section). That whole interface is getting a massive UX overhaul, and we’re using that opportunity to give React a firm toehold in the product.

We have also drafted specs that describe how we will describe forms to React from the server. We have always rendered form elements using getCMSFields(), which essentially returns a string of HTML. We’ll need to create some kind of schema that our React layer can understand and can translate into components.

Lastly, we’ve looked into how we can allow third party developers to modify the presentation and behaviour of our core React components, and we have a lot of ideas, specifically around dependency injection. Right now, BottleJS looks like our best candidate for injection. This will allow you to specify your own React component to use in place of another one, which will be critical to getting features like GridFieldComponents to work in future versions of the CMS.

Want to learn more?

A great place to start is the React homepage. Additionally, we like the scotch.io tutorial and the Codementor course.

Header photo by Wonderlane.

About the author
Aaron Carlino

Aaron Carlino, better known by his whimsical pseudonym Uncle Cheese has been an active member of the SilverStripe community since 2007, and has never looked back. In that time, he has established himself as a support resource, mentor, and contributor of some of the framework's most popular open source modules.

Post your comment

Comments

  • Awesome idea to use BottleJS to provide injection hooks for third party components. Let me know if you need any help integrating.

    Posted by Stephen Young, 02/04/2016 9:06am (8 years ago)

  • Nice article dude! I'm new to silverstripe and it looks like i've hit it at a good time!

    Posted by Mike Haddon, 10/03/2016 10:13am (8 years ago)

  • Your link to JSX being its own library has nothing to do with React; that is a totally different language that happens to also be named JSX. You probably want to point to https://babeljs.io/docs/plugins/transform-react-jsx/. JSX is really just a Babel transform, and to your point, with a simple pragma comment /* @jsx foo */ you can make JSX compile to foo('li', ...) instead of React.createElement('li', ...).

    Posted by Ian Obermiller, 08/03/2016 10:58am (8 years ago)

  • I'm excited about this!

    Posted by Darren Inwood, 26/02/2016 5:58pm (8 years ago)

  • Thanks for a great post, Aaron.

    Comparing the ReactJS components to the SilverStripe includes components clarifies this 100% - I'm looking forward to replacing all of my front-end jQuery with ReactJS in the coming months.

    Posted by Tim Larsen, 26/02/2016 11:45am (8 years ago)

  • A philosophical aside perhaps, but JavaScript was originally designed for image rollovers and status bar scrolled and arrays were base 1.
    So pretty much everything is not what JavaScript was designed for.

    Posted by Grandpa Simpson, 24/02/2016 12:50pm (8 years ago)

RSS feed for comments on this page | RSS feed for all comments