Stencil + Redux: slot components setup

Michał Kaliszewski
4 min readJan 13, 2019

I’m leaving here a few words about Stencil with Redux. Sure it was already covered in docs and nice starter project but I feel like it would be good to: first say that it’s really working under some url on production for billions of happy users and second show simple example on how it can be used. Plus while developing I found weird issue in Stencil version 0.16.0 which I will show and also leave a fix for it since it got me googling and asking questions on Stencil’s Slack channel.

Some requirements

Requirement was to implement a standard REST API web client that would display a list of resources. Client should be able to search for specific resource, filter sort and paginate the results. Let’s say that resources are products. All those actions are achieved on same resource so we had a url that looks sth like:

http://some-domain/products?productNumber=123&productName=productX&sortBy=productName&pageSize=5&pageNumber=2

All query parameters were optional. Since whole project was done based on platform then additional requirement was to have server side rendering. At least on some places. This is because platform solutions are supposed to be as much configurable as possible and this is achieved using some backoffice solutions. So you basically end up having a lot of global or page specific or even backend component specific configurations and translations. That would definitely be an overkill to pass all those configurations to frontend client so usually SSR is used.

Tech stuff

We went with Stencil mainly because of offered mix of developer experience with agnostic nature (web components on vanilla js) since project should be a core solution for other business products. SSR was achieved with Stencil’s slots: https://stenciljs.com/docs/templating-jsx#slots

According to docs Stencil supports two different methods for achieving shared state:

We went with Redux. Like any other technology it has some benefits and disadvantages. Personally I like it because it makes components truly independent and selfish. I mean as a component I should have limited knowledge, only know about the things that are strictly required for my existence and definitely do not have knowledge about other guys that are at the ‘same level’. Also as a good component I should only do stuff that I have to and not more.

In our case we will have a product table component and product search component that are on the same level. Going with standard DI approach (injecting some ‘productService’ that calls API to components) we end up having search component calling injected service, receiving response and calling some ‘update’ method on product table. Receiving response and updating table is already too much! The cleanest what we can get is emitting event with search results to table but Redux makes it better:)

Code

Let’s start with GetProductsRequest definition. This class will hold all ‘get products’ API operation parameters and will be used to construct request url:

Now we need our actions and reducer definition.

As you can see we will have three actions to call the API and process the response. Now that we have them, we can define our store:

…and we configure it in our root component. After that, the store will be available in every child component.

At least it should be available… Anyway as you can see our root component is abstract. For now it has only store configuration and slot in render method. This is where our real, business components will go:

  • product-search where we will declare Redux action to get products based on request object (this guy will be actually shared over all components that call ‘getProducts’ for instance product-filter)
  • product-table which should basically rerender as a result of changed state

Everything in place so we can give it a try on our product page:

Issue…

This should already work according to docs but it throws an error: “Cannot read property ‘subscribe’ of undefined” in product-table. It means that Redux store was not yet configured while we are trying to call mapStateToProps on it. If we now put a log line in st-will-load lifecycle hooks we will find out that the sequence is as follows:

table will load — > search will load — > root will load

which does not make sense since according to docs it should be opposite: root component will load first then child components are rendered (will load -> render() -> did load) and then root is considered loaded (did load hook). The issue which we are experiencing here is known, you can take a look at this github issue.

Hotfix

I created a small repo to show the issue and how we can fix it until it is not fixed in the compiler itself. Namely either keep naming convention or use componentOnReady on root component in child components that are loaded first. Let’s try that in table and search components:

Now issue is fixed and lifecycle is as follows:

root will load -> search will load -> table will load

Console error disappears and we have working example of Stencil + Redux setup with SSR (slots). You can check it out at this github repo. I will add there some additional stuff like filtering, load more, sorting to make it more practical starter:)

--

--