We spent 18 months building the foundation for Engage by Shopgate and, at the beginning of last year, we took a step back to reflect on what we had achieved. We took a deep dive into our architecture and carefully considered feedback from our clients and partners.
With the help of this feedback, we revisited some of our earlier work and focused on streamlining key internal functionalities and improving performance and extensibility.
How big are the changes we made?
We’re committed to minimizing changes to existing APIs to prevent burdening our clients and partners with refactorings. With this in mind, we simplified our feature code and eliminated outdated third-party tools.
What exactly has changed?
Proactive vs. Reactive
One of the biggest improvements is how navigation works inside the app. In Shopgate’s Engage versions 5 and below, the navigation is proactive, meaning the developer can only apply business logic to a navigation action after it happens. As of version 6, the navigation is reactive. This means that the developer can intercept a navigation action and override it before it happens.
Better and faster routing
We converted the frontend router from a custom implementation to Conductor, a third-party module that provides all the features necessary to deliver a powerful, blazing fast React application.
Additionally, we updated React to its newest version, 16.6.0, which provides powerful new ways to optimize our application.The new Context API that React uses optimizes flexibility when accessing data from a deeply nested child in the tree. This upgrade results in the following improvements.
Eliminating unwanted reducers
Previously, we stored UI-related information in our frontend store to avoid passing React props down the tree. Additionally, we built our custom router implementation on top of React and Redux. This construction limited overall flexibility. After changing the router implementation, we could finally remove some unwanted reducers:
- history (has been completely replaced by the router itself. Redirect information is now part of the state object in every route)
- navigator (redundant after our refactorings)
- ui (redundant after our refactorings in favor of React’s context API)
- view (redundant after our refactorings)
Navigation between pages
The application’s internal navigation processes have changed slightly. The primary navigation process using the
<Link> component is still the same, but other processes, like redirecting or authentication-protected navigation, have changed.
Relocated React component
We relocated the
<Link> component that renders links inside React. Before, it was located with our custom router implementation at
@shopgate/pwa-common/components/Router/components/Link. Since we removed the custom router, we moved the
<Link> component to the common components, under
The primary way to navigate programmatically is to dispatch a Redux action. This triggers a specific RxJS subscription, which handles the navigation action and then performs the router action.
This method can be used inside a connector or in any RxJS subscription.
Redirects no longer use the store. Instead, a collection is used to set or unset the redirects as needed.
This redirect registrations can be performed at any place in your code. We usually hook them up in a subscription to the appWillStart$ stream.
Authentication is no longer handled by React components and is also no longer bound to protect routes only by the login route.
Similar to the redirects, we already provide a collection to register authentication protected routes.
Before the router opens the /checkout route, the application checks if the user is logged in. If that is not the case, the router opens the /login route and stores the user’s original target location until after the login completes.
NOTE: The developer decides when and how to open the original target location. Sometimes, one has to wait for a server response or wait for a user process to finish. This can’t be automated.
Any component now has access to the Route information. This information is accessible through the new Context API, introduced in React v16.6.0.
NOTE: This example uses the new contextType property. You can, of course, use an HOC to inject the context properties, via props, into a component. The React Documentation describes how to achieve this.
It is also possible to directly ask the router for information regarding the history stack.
Routing streams are now much simpler to use and construct. The available streams can be found at @shopgate/pwa-common/streams as described in our Reference Documentation. Here is an example of how to create a new stream:
Some of our component implementations have been simplified or completely reworked. The most important changes are discussed below.
NavDrawer (open and close)
Before our refactorings, it was a real challenge to open and close the Android style Navigation Drawer (a.k.a
<NavDrawer>). We changed that by introducing a static API into the component that you can use to open and close the component:
There are many reasons to show a loading indicator on your page. For example, when the user puts a product into the cart.
In version 5 and below, the Product Page shows a loading state while the cart updates in the background. This is not a good approach because it blocks the user from interacting with the UI.
A better approach is to put a loading state on the Cart Page, so that when the user adds a product to the cart and immediately switches routes, only the cart page is blocked until it updates.
This approach is now possible with version 6. We introduced the LoadingProvider, which allows you to set a specific route as loading via the route’s pathname. Here is an example:
<AppBar> component is a replacement for the <Navigator> component. The main difference is that it is not a global component inside the theme anymore. Now, every Route has to render its own AppBar.
Every theme comes with several presets that you can use in your custom Route:
Next, let’s explore how the AppBar can be accessed from an extension.
Creating new Routes
The process of creating new Routes has been simplified. With the previous described Context API and our pre-defined Theme context it is now super easy to define custom Routes from an extension:
We provide a complete guide on creating custom routes in our Developer Documentation.