When React Meets OOP

Juntao Qiu
ITNEXT
Published in
5 min readOct 26, 2022

--

In this article, I’ll discuss a case study of mixing Object-Oriented Programming with React (Functional Programming) and demonstrate how it can improve readability and extendibility.

There isn’t a universal way of writing and forming your React application, but I do see some encourage people to write more functional programming-style code in React while others put everything inside view, and I believe that we should apply what has been proven in other software fields (especially Design Patterns and OOP in this case) and I think these practices can also be beneficial in React code.

I got a video version too if you prefer watching instead of reading

[Update 11.Nov.2022] Now that the whole series is done, I’ve created a book for it on leanpub. You can get a copy with this link for 30% off.

Background

Several months ago, I published two blog posts to summarise what I had found during a refactoring journey in the project. I distilled some fun facts from the origin project and removed all the noise, and essentially the feature was all about rounding up logic in different markets and around that requirement how did we, as developers, refactor the code to a state that is easy to adopt any new market or new round-up algorithm.

Payment Screen in the web application

For example, in the Australian market, it’s common to ask if a user would like to donate $0.1 when the order is $19.9. But the same logic doesn’t work in Japan market as 0.1 Yen is literally nothing. We need to round up to the nearest hundred, and for Denmark, we need to round up to the nearest tens.

That’s all for the theory. And recently when I put this together as a video tutorial, I found something interesting. My original plan was to reproduce these blog posts to a video that is easier for someone who prefers to learn by watching videos, but as I’ve done this particular case in so many cases, after the recording, I was hit by another idea (which I believe would not happen if I do this for the first time) — the scenario is similar to one of my backend project feature, and I was wondering what if I can apply the same design pattern in my React code too?

To be more specific, in my React code, I need to expose a countryCode in the props:

countryCode in props

And as you can guess, I need some logic inside Payment to format the label and calculate the round-up amount:

Use currency map to get correct sign

And also I need to switch to a different algorithm for a different country in my custom hook:

Similar logic in useRoundUp hook

(I know what you want to say, but don’t worry, the above code is simplified, and I don’t use let at all).

The problem

The problem here is that whenever a new country code is added to the system, I need to change a few places. The formatting logic (for the currency sign), the round-up logic (to integer, ten or hundred), and some helper functions.

This is pretty much what people call shotgun surgery. Basically, you have to modify a lot of places to implement a small requirement change.

Shotgun Surgery smell

Strategy Pattern

And it’s time to introduce the main thing I’d like to discuss in this article: strategy pattern. That’s my second-most favourite pattern (the first one of course is the Observer Pattern). Essentially, the strategy pattern is to have a few strategies that implement a common interface, and that interface has methods that can replace these if-else checks (or map looking-up) spread in the code before.

And typically in OO languages, we use polymorphism to use different instances of that interface at runtime.

For example, in the image above, the bar with different colours represents checks like if(countryCode === 'JP'), so instead of doing that, imagine we have a dedicated class for Japan, and in the class, it has a method like getCurrencySign that returns ¥. And at the same time, we have a sibling class that would return $ if the getCurrencySign was called on that class.

Using Strategy Pattern

That means we have only one place to change anything for Australia, Japan or whatever the new market expands into!

React context is the icing on the cake

Back to our React application, once I have extracted three classes RoundUpStrategyAustraliaRoundUpStrategyDenmark and RoundUpStrategyJapan. The only thing l need to pass in is:

Pass in a strategy as a whole object

And when I showcased (actually showed off) to my wife what I’ve done, I soon realised that I could do even cleaner with React Context.

Define Strategy in Context

And I can wrap the Payment around RoundUpStrategyContext like:

Wrap Payment with context — and then you can dynamically switch

For Payment itself, it’s pretty much what I have for the initial version. The only dependency is the super abstracted RoundUpStrategy interface.

The Classes Diagram

And finally, I made a UML diagram to demonstrate what I have alongside the React code.

The classes diagram of Payment application

Benefits

I think there are many benefits of this OOP + React approach, but I’ll name a few here:

  • The much cleaner boundary between view and business logic
  • Testability for both view and the classes (or algorithm)
  • We can push the classes extracted entirely to the backend without modifying a line of code

Summary

Although Object-Oriented Programming is rarely mentioned in React, it doesn’t mean we should ignore the proven patterns and concepts in other fields of software development, especially when they fix our problems.

Using interfaces, classes and context in React, we can significantly simplify a lot of complicated logic from our view and hooks, while maintaining a high level of readability and testability.

If you would like to read more about the topic, I have a book about it here in leanpub too.

--

--