silverstripe / silverstripe-cms

Silverstripe CMS - this is a module for Silverstripe Framework rather than a standalone app. Use https://github.com/silverstripe/silverstripe-installer/ to set this up.
http://silverstripe.org/
BSD 3-Clause "New" or "Revised" License
511 stars 333 forks source link

RFC-8: Adding ReactJS to SilverStripe CMS #1301

Closed flashbackzoo closed 8 years ago

flashbackzoo commented 8 years ago

Adding ReactJS to SilverStripe CMS

We’re suggesting adding support for ReactJS to SilverStripe CMS. This would be done by making https://github.com/open-sausages/silverstripe-reactjs-common a dependency of CMS. Developers could then build SilverStripe CMS UI components using ReactJS for example https://github.com/open-sausages/silverstripe-assets-gallery.

Why frameworks are good

So why do we even need to have a framework? There’s are a few reasons.

Faster than a speeding bullet point.

Rendering UI on the client reduces the number of HTTP requests made by the application. Take sorting a list of blog posts for example.

Assume posts are sorted from newest to oldest by default. We’re looking for our post 'Apple Pie' so want to sort by post title. If we’re rendering HTML server-side, we need to send a request to the server along with the new sort order. On the server a controller handles the request, we get a list of blog posts ordered by title from the database, then render a template and pass that back to the client, in the response.

This process is repeated each time the user changes the sort order. Moving some logic and the template rendering to the client speeds up the user experience, by reducing the number of HTTP, and database requests.

Consider the same list rendered client-side. When our blog posts page is requested, instead of the server sending us HTML, we get sent JSON. Maybe something like this:

{
    "posts": [
        {
            "title": "Pavlova",
            "date": "Fri Oct 16 2015 12:00:00 GMT+1300 (NZDT)"
        },
        {
            "title": "Chocolate Brownies",
            "date": "Thu Oct 15 2015 12:00:00 GMT+1300 (NZDT)"
        },
        {
            "title": "Apple Pie",
            "date": "Wed Oct 14 2015 12:00:00 GMT+1300 (NZDT)"
        }
    ]
}

Our client-side code stores a copy of the post array, then renders this data in a template. When the user selects a new sort order, we already have the data in memory, so the template can be re-rendered without needing to send a request to the server.

Maintainable and modular code

JavaScript frameworks make it easy to write code in self-contained "component" modules. We could have DataList, DataItem, and SortFilter components for example.

A DataList contains DataItems (child components) and a SortFilter which updates the order of DataItems in a DataList. Each component has clear responsibilities and looks after rendering a single block of HTML, which is a representation of that component’s state. This means we don’t get into situations where an HTML element is being modified by "something" and it’s very difficult to tell what that "something" is.

A component’s HTML represents the component’s in-memory state. When a component’s state is modified, its template is re-rendered. Keeping state in memory is not only faster than doing DOM lookups which check data-attribute values, it also means there is a clear place in the code where modifications should be made. You avoid having multiple scripts modifying a single block of HTML, which is super messy, hard to debug, and leads to lots of race conditions.

Separation of concerns

We currently have markup scattered across PHP, SilverStripe template (.ss), and JavaScript files. Using a framework pushes markup and associated logic to the client. This lets the server focus on request handling, database operations, and serving up data to the client. The client handles how the data is displayed.

Why the ReactJS library is a good choice

Composable UI

The markup and logic which controls a component is encapsulated in each component. Components deeper than the top level need not know about how their data is retrieved or persisted. This leads to the ideal environment for creating modular, reusable components.

Because ReactJS provides only UI, it doesn't impose any opinions or restrictions on the flow or structure of data. This makes React highly interoperable in an existing Javascript application. While full stack frameworks such as Angular, Backbone, and Ember impose new concepts on the entire application (e.g. controllers, models, and directives), React components can be composed any way you like, which makes it play nicely with other UI components, whether legacy or tangential technologies.

It's also possible to compose components to allow the injection of third-party components at key points in the application lifecycle. For details of how this can be done, take a look at https://nylas.com/blog/react-plugins.

Established architecture

There are many great patterns promoted by the ReactJS community, like the use of immutable data types and unidirectional flow of data. Many of these are summed up in the official, recommended architecture, called Flux. This allows for consistent architectures across modules.

Testable

The declarative nature of JSX makes their expected results obvious and easily calculated. ReactJS unit tests can be integrated in the CI services we already use, to run alongside PHPUnit tests. We’ve also got a working example of this here and here.

Summary

Ultimately, the use of ReactJS provides an opinionated guide for how module authors can build efficient UI components. We have already begun to formalise how these modules operate (in the examples linked above), and we’re constantly refining the approach we take so that the first official SilverStripe ReactJS code can become a beacon for module authors. A place for them to learn how to overcome their fear of a new JavaScript library in SilverStripe core.

camfindlay commented 8 years ago

@tractorcow can you please tag with one of the new RFC tags.

sunnysideup commented 8 years ago

I would love to add React to my "product category" pages in my e-commerce module (https://github.com/sunnysideup/silverstripe-ecommerce). It would be great if some basic "best practice" ideas and "SS to React" / "react to SS" concepts would be available... YES Please ;-)

flashbackzoo commented 8 years ago

Awesome. At this stage you could make your module require reactjs-common. Then you'd be able to start building React components in you module. If you have any problems setting it up, open an issue, and I can help you out :smile:

sunnysideup commented 8 years ago

ok - great. THANK YOU. I have never used REACT so I will need to see how the basics work first.

It will probably take me a few months, but we will get there.

For e-com, Basically, each product category has 0 - 1000+ products. Right now they are served as 10 per page, sortable and filterable by a bunch of sorters and filters. (e.g.SORT: latest arrival, lowest price, etc.. FILTER: cross-referenced product categories and brands).

From what I understand, SS basically provides JSON "array" for all of them and then REACT is going to do the filtering and sorting, as well as presentation markup? There are a couple of questions that come to mind immediately:

I am really excited about this as it could take my e-com to the next level.

THANK YOU for the INSPIRATION!

flashbackzoo commented 8 years ago

From what I understand, SS basically provides JSON "array" for all of them and then REACT is going to do the filtering and sorting, as well as presentation markup?

That's correct.

Have a look at the React docs on displaying data to see how templates work.

You wouldn't have to send all products back at once. You could send for example 15 products at a time. Have a look at https://github.com/open-sausages/silverstripe-assets-gallery/blob/master/code/AssetGalleryField.php

jonom commented 8 years ago

Better performance and a cleaner separation between Framework and CMS - sounds like a win win!

mediabeastnz commented 8 years ago

This sounds great! Nice job @flashbackzoo

stevie-mayhew commented 8 years ago

I am for this RFC as a basic of Use React

I believe another RFC for the way in which modules will work and be structured should be required if this is accepted. Its extremely important this is approached with a well thought out, well structured and ultimately extensible environment or there are massive holes which can be dug in React. I've dug them.

I am particularly concerned with some of the decisions which appear to have been made in the examples linked above, especially the lack of a flux implementation which is mentioned in this RFC - but those concerns can be raised in the following implementation RFC if (when) this RFC is accepted.

Seems like it already has been accepted though: https://twitter.com/StripeConEU/status/654947127697788928 :wink:

flashbackzoo commented 8 years ago

@stevie-mayhew Totes. I've worked on other projects (Backbone in particular) where using a 'bespoke' architecture is a nightmare. Lessons learned :smile:

I think some form of Flux architecture will be put forward in that RFC.

sminnee commented 8 years ago

Lol, that slide may have overstated that slide a tad. I had basically said "we're considering ReactJS and an RFC has been posted last night, so you should all go check it out!" ;-)

unclecheese commented 8 years ago

How do we plan on addressing the "least common denominator" problem we had with jQuery, where developers had to dumb down their modules for a version of the library that was several months, or years, out of date? React moves fast, especially in its < 1.0 stage, and API changes are frequent.

A simpler way of asking that is could we upgrade the SS version of React in a point release, or would that be an API change?

flashbackzoo commented 8 years ago

This will help https://github.com/silverstripe/silverstripe-framework/issues/4372. We'll be able to upgrade dependencies much more easily.

We could upgrade point (non-breaking) releases of React in SilverStripe point releases.

xini commented 8 years ago

I'm always for making the CMS faster. I can't comment on what library is best to do the job, but from what I've seen so far react seems to be very flexible.

I have two issues I would to raise though:

You mention separation of concerns, currently having mark up in php, js and ss files. How does react help here? We still need templates, we can't make people not producing mark up in php and js will build even more of it. If separation of concerns is an issue, this doesn't seem to be the solution.

Second, we need to make sure modules don't require react for the front end website. Websites still need to work without js.

Cheers

flashbackzoo commented 8 years ago

Thanks for raising this, I think there are other people keen for some clarification on this too.

React won't be required by CMS modules or the font-end of websites. Existing CMS JavaScript can co-exist and interact React components. See https://github.com/open-sausages/silverstripe-reactjs-common/pull/4 for how we could interface React components and legacy JavaScript in the CMS.

By separation of concerns I mean everything concerning the 'view' layer of a component, the HTML, JavaScript, (CSS? I'm still not sold on mixing that with component code) are contained in the component's JavaScript file. This means a developer wanting to modify a component's 'view' layer can do so in a well defined place. This doesn't mean we remove server-side rendering altogether, it means we have the option to render views on the client as well as the server. And in some cases it might be a combination of both.

torleif commented 8 years ago

ReactJS is the wrong fit for SilverStripe. At heart SilverStripe is a MVC framework and reactJS renders the view. I doubt it will make the CMS faster, network profiling reveals almost all main offenders are database calls. Rendering components is minimal. SilverStripe instances with an optimized back end stack are lightning fast. Rendering fields in JavaScript will also affect usability if you want to use these components on the frontend.

I do agree that something needs to be done in regards to the frontend UI framework. Finding documentation for entwine is impossible making the development process for extensions frustrating, leading to unstable frontend components of lower quality.

However given the option, I would choose ReactJS over the current frontend architecture. My preference would be backbone.js as it models SilverStripe's front more closely. I can't weigh in on other frameworks however.

Frankly the core issue here is the perceived performance of the CMS. It's been a major complaint about the product in general and desperately needs to be addressed. If this were to go ahead, it would give the developers opportunity to implement precaching for the pagetype views, sitetree, and model admins. This would give the impression of the CMS being snappier.

flashbackzoo commented 8 years ago

I don't agree that Backbone is a good fit. The pub-sub pattern it promotes, emitting and subscribing to events, makes the codebase difficult to maintain and debug once your application is large. You end up in a similar situation to what we have now, where you can never be sure where changes are coming from. Re-rendering is also much slower than React because you're replacing much more of the DOM each render. Backbone components aren't composable, we've got a large ecosystem of modules, so it's essential to have components which can consume other components. Backbone is also better suited to single page apps, which have front-end routing, which isn't how SilverStripe works currently.

Rendering fields in JavaScript will also affect usability if you want to use these components on the frontend.

What are some of the specific usability concerns you have?

torleif commented 8 years ago

You're correct about backbone becoming more unusable as the application grows, I've ran into the issue using frameworks like it.

For usability requiring javascript components for a CMS front end components would lead to JavaScript conflicts, and violate WCAG 1.0 and the governments 'graceful degradation' usability requirement.

flashbackzoo commented 8 years ago

I'm not sure what you mean by "would lead to JavaScript conflicts" can you elaborate?

Good point about WCAG http://www.w3.org/TR/WCAG10-TECHS/#tech-scripts. We're only suggesting React be used for the CMS interface, not making it a requirement for the public facing site. Like I mentioned above, this would not replace server-side rendering, it's an addition.

How SilverStripe field classes would look is a bit outside the scope of this discussion, but fields would be able to provide data about themselves (JSON) for client-side rendering, and still have the ability to render server-side. So fields would still be accessible for devices with JavaScript disabled for example.

flashbackzoo commented 8 years ago

This is in framework master (Campaigns section) and is being used in the AssetAdmin module.