I’ve ended my previous post I’ve mentioned that I would add redux in a Sitecore-ReactJS mix. That is what I’m going to talk about in this post.
An original solution helps us to reuse ReactJS views on the server which actually could save us time in development and maintenance. It was more about static content generation. While most of the sites I’m working with requiring more than that.
Take an e-commerce storefront, a travel site, or utility customer portals, a customer would demand a lot of interactivity in all of them. Some would even be implemented as a SPA and use headless CMS approach. The last option isn’t the best case for Sitecore as we losing a lot of functionality built around the presentation layer.
From my point of view, truth should be somewhere in between. In a hybrid approach, when we can generate pages from CMS and make them dynamic. And as we already using ReactJS views on a server adding stores, dispatchers, and actions from Redux will help us to create these dynamics.
If you are interested in other details you could watch videos from its creator, but at this point, I need to go back to the Sitecore solution.
As you remember in the previous post we created React views (represented on the diagram above by a green box with $ signs). So we need to create actions, reducers to mutate our model, store that would keep it, and tie it all together.
I’ll start small, with simple components: counter and timer. The first component is Timer, it has one event ‘onTick’ that is generated by an internal timer. The event produces an action, that is processed by the timer’s reducer and increment seconds counter in a model.
Counter instead of the internal timer has 2 buttons, that increment and decrement another stored value (see code in the gist). But those two components are not alive yet.
Binding Redux Components
The next step is to connect those components to a store and make sure that React engine knows that it needs to update the component. In as SPA you most likely would have Provider component that wraps your app. It pushes store and dispatch() method to nested components via the context. The Sitecore layout can contain React and MVC components and they can live in pretty far parts of the page, which makes it difficult to use Provider. Instead, I’m using subscribe() method of a store, and pushing render() method there for each component. Also, I’m explicitly passing store object and dispatcher function to each component. This is how store created:
Rendering Redux Component on a Server
Redux has very little to do with a server-side. The only thing that you need to consider on the server is a state, that is provided by a store. Nevertheless, I need to create two functions to render components to as string and to a static string (no react data attributes added).
I’m using a decorator here to wrap all components and add render functions. You might notice, that those functions duplicating the functionality of ReactJS.net. By exporting those functions out of the webpack module, I’m able to fully control how I pass params to those components. Make sure that they know about a store, a dispatcher, and other goodness. On the other hand, I’m minimizing the amount of data that should be passed to this component from the server. It makes generated JS code a bit cleaner. As you see above, I do not need to generate render() methods and subscribe it to store- it is all done via the decorator.
I have one last thing that I need to mention before we move to back-end code. I get rid of the expose module and generating global variables from a webpack module itself.
Server-side code in .NET. Stepping Aside from ReactJS.NET
The main idea behind ReactJS.NET is to provide a possibility to render JSX from a .NET server. The ability to use a compiled package was secondary. However, for my approach, it is a key and this means that I need an underlying JS engine and JSPool more than the rest of the library.
ReactJS.NET is creating render() methods on the fly for required components, while but we exposed them already from a WebPack module as described above. To use them, we need to overwrite the implementation of the IReactEnvironment interface. It has a factory method for IReactComponent that we use. Finally, we need to register them all via IAssemblyRegistration.
Note: I’ve found a bug in IoC implementation of ReactJS.NET. It won’t allow you to rewrite registered implementation for an interface. As a workaround for that, I’ve recompiled the original library without a default implementation.
I changed an implementation of a CreateComponent method to return ReduxComponents in ReduxEnvironmentclass. ****
That is it. We have no changes in controller creation or a registration in Sitecore. It all could be done in the same way as described in the previous post.
You might have seen this GIF already in my Twitter account as a spoiler to this post. Redux is connected to DEV tools in chrome and functioning as expected.
- We exposing decorated components extended with rendering methods to Sitecore/.NET from WebPack.
- Methods in the decorator providing functionality for generating DOM elements and HTML as a string. Methods are already connected to Redux store and dispatcher.
- A default implementation of the ReacJS environment in .Net is overridden with Redux implementation. Now it uses rendering methods from the JS module.
- I need to try Dynamic Placeholders with this implementation.
- Test performance of the JS engine in Sitecore. Maybe compare different implementations.
- Caching should be revisited, as we generating JS initialization scripts in a request context. (I wrote about that a while ago)
Also, the amount of code is growing as usual, so I would think about how to create something on GitHub.
Follow me on Twitter @true_shoorik. Would be glad to discuss the ideas above in the comments.