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 in on server which actually could save us time in development and maintenance. It was more about a 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. Last option isn’t the best case for Sitecore as we losing a lot of functionality built around presentation layer.
From my point of view, truth should be somewhere in between. In a hybrid approach, when we are able to 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 and action, that is processed by 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
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. Sitecore layout can contain React and MVC components and they can live in a pretty far parts of the page, it 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 decorator here to wrap all components and add render functions. You might notice, that those functions duplicating 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 an 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 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 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. In order to use them, we need to overwrite implementation of 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 original library without a default implementation.
I changed an implementation of a CreateComponent method to return ReduxComponents in ReduxEnvironmentclass.
ReactJS.NET generating an init script at the end of a component processing. As a result, that nested component initialization scripts will appear first as they will finish rendering earlier. On the other hand, rendered HTML for a placeholder is passed as props to react component. So HTML that you need to bind you nested component to, might not exist if a parent component isn’t initialised. Reversing an initialization order solves the problem.
That is it. We have no changes in controllers create or 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 ReacJS environment in .Net is overridden with Redux implementation. Now it uses rendering methods from js module.
WHAT’S NEXT ?
- 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 how to create something on GitHub.
Follow me on twitter @true_shoorik. Would be glad to discuss ideas above in comments.