circuit

React Hooks and the “Observer” Pattern

How to integrate the “Observer” pattern when using React Hooks




Photo by JR Korpa on Unsplash

The arrival of the React Hooks has led to a multitude of changes in the way we design and program applications with React:

  • they have allowed to add internal state to the functional components, so that these have begun to replace the “classic” components;

  • they have encouraged the use of a declarative programming style due to its functional nature;

  • and have simplified the way in which we can reuse logic throughout the entire application, something that using Class components was somewhat more complicated.

It is around this last point that this article revolves around: the way we can use hooks to implement some of the best-known design patterns, so that we can build applications that are easier to maintain and scale.

Let's see how!

Introduction

If you have landed in this article without knowing what a design pattern is, I will try to summarize it briefly although I recommend that you go to any of the following links to deepen this important concept in the development of applications:

A design pattern is a general and reusable solution to a common problem within a given context in the design of an application.

That is, the design patterns provide solutions to certain problems that appear on a recurring basis when considering the architecture of an application so avoiding having to constantly reinvent the wheel.

Its power is that its use and application does not depend on the language in which we are developing but, by abstracting the solution through class diagrams, we can implement them in any software application.

Another advantage of design patterns is that they provide a common language when communicating with other developers since each of them defines a specific name to refer to it as well as the elements that participate in the proposed solution. This allows that, regardless of the type of application developed or the language used, the same “dictionary” can be used to refer to certain concepts. In this way, the code is “self-documenting” allowing future people involved in its maintenance to quickly identify the solution implemented.

React Hooks and the “Observer” pattern

In the pattern “Observer” an object, called “subject”, maintains a list of its dependencies, called “observers”, and automatically notifies them of any change of state, usually calling one of its methods.

Thanks to this pattern we can have more decoupled applications (since the observers do not need to be attached to the subject) and maintainable since each Observer is independent of the others, so that a change in the functionality of one of them does not imply the modification of the rest.

Its UML scheme of classes and sequence are as follows:

By Vanderjoe — Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=61809260

Now imagine that we want to develop an application in React where users can visualize the weather in a city in different places of the application, for example, within a sidebar with their favorite cities and in a list of cities.

Therefore, we would have two components:

  • FavouriteCity, to show the temperature of a city within the sidebar of our application.

  • CityTeaser, to show the temperature of a list of cities.

It is convenient that the state of the climate in a city is centralized and that both components receive it, that is, free them from having to request it (which would end up causing duplicate code). And how can we centralize the state of the climate in a city so that both components are updated every time a change occurs? Thanks to the pattern “observer”:

Following the diagram:

  • Weather that acts as Subject of the “observer” pattern centralizing the weather in a city.

  • FavouriteCity and CityTeaser acting as Observers by receiving through their onWeatherChanged method the update of the weather coming from the Weather class. These observers can request to be notified ( notify method) by means of the attach method of the “subject”.

Having this clear, we will first implement the code of the Weather class. In order to be able to define new types and interfaces as well as to be able to type the arguments of each method I will use Typescript as a language although if you want you can write your own version in Javascript without any problem.

Weather Class

According to the diagram, the WeatherSubject class must have the following methods:

  • attach, so that an observer can register and receive notifications when the temperature is updated.

  • detach, to allow observers to unsubscribe when they no longer wish to receive more status notifications (and also prevent memory leaks caused by keeping references in memory to objects that are no longer being used).

  • notify, a method that will notify registered “observers” every time a weather update occurs.

  • In line 1 we are defining the type of observers that can subscribe to the Weather class. In our case, it will be a function that will act as a callback, receiving as an argument the temperature obtained.

  • In lines 7 and 11 we define the body of the attach and detach methods so that the observers can subscribe and unsubscribe to status updates.

  • On line 15 we define the updateWeather method that gets the weather every 1 second and notifies the “observers” through the notify method.

  • On line 33, the notify method traverses the array of observers and invokes them with the temperature received.

FavoriteCityComponent

Our FavouriteCity component will act as an “observer” so that:

  • When mounting, you must subscribe as an observer in the Weather class through the attach method.

  • When unmounting, you must unsuscribe from the Weather class by means of the dettach method and thus prevent it from being invoked when it is no longer being used.

This is where the React Hook come into action. We will create a functional component that will use two hooks:

  • useState to store the current temperature. In addition, this hook will provide us with a function to update the current temperature that we will use within the callback that we will register in the WeatherSubject.

  • useEffect to subscribe and unsubscribe from the WeatherSubject.

  • In line 6 we define a state for the FavouriteCity component that will store the current temperature and allow us to update it using the setCurrentTemperature method.

  • On line 8 we define the callback that we will register in WeatherSubject: a function that receives as a argument a Number with the temperature obtained and that will use the setCurrentTemperature method to update the status of the useState hook.

  • On line 12, using the useEffect hook, we subscribe once (using an empty array [] as a second argument of the hook) to WeatherSubject by means of its attach method and at the moment when the component is disassembled, we unsubscribe using the detach method.

  • Finally on line 19 we paint the temperature we have stored in the useState status hook.

CityTeaser Component

As you have seen in the previous component, the Observer pattern has allowed us to decouple the climate update from the FavouriteCity component. In addition, thanks to the useState hook in just 20 lines we have solved the problem thanks to the callback it provides to update the status.

So that you can see how the useReducer hook also integrates perfectly with this design pattern, I will implement the CityTeaser component in this way:

In this case we have required some more code:

  • In lines 4 and 6 we define what our reducer function will be: it will receive as a first argument the current state and as a second argument the action that has been launched represented by an object with the type property and the temperature property with the temperature value at to update.

  • In line 14 we use the useEffect hook to obtain the dispatch function and then we define the onTemperatureUpdated function to dispatch the temperature received each time WeatherSubject notifies us.

  • And finally, as in the FavouriteCity class, we use the useEffect hook to subscribe and unsubscribe when the component is assembled and disassembled respectively.

Final conclusions

The observer pattern is probably one of the most widespread patterns in application development due to the advantages it provides:

  • Low coupling between the components that make up the application.

  • A single sense for the transmission of information and state, that is, the Subject is responsible for monitoring the status of a certain part of the application and for notifying the observers who have requested it.

And thanks to the React Hooks it is very simple to implement it in our applications as I have tried to show in this article.

I hope this article has helped you discover another way to use the React Hooks as they have come to stay.




Continue Learning