Using RxJS/BLoC as a state management solution for React
Tags: webdev, react, javascript, programming
Published: 2022-11-25 12:21:01

Hello fellow programmers 👋. in this article, we will discuss how we would use the [RxJS](https://rxjs.dev/) library to implement the BLoC pattern as a state management solution for a React project. 

#Table of Contents

- [Prerequisite knowledge](#Prerequisite-knowledge) 
- [What is BLoC?](#What-is-BLoC?)
- [Creating a Todo app using BLoC/RxJS](#Creating-a-Todo)
 - [App initialization](#App-initialization)
 - [Folder structure](#Folder-structure)
 - [Creating the models](#Creating-the-models)
 - [Creating the BLoC's](#Creating-the-BLoC's)
 - [Creating the hook](#Creating-the-hook)
 - [Creating the view](#Creating-the-view)
- [Considerations and closing remarks](#Considerations-and-closing remarks)

#Prerequisite knowledge <a name="Prerequisite-knowledge"></a>

Before going through the content of this article, it is assumed that the reader has prior basic knowledge in Rx, at least with observables and observers. The article will try to briefly go over these concepts, if needed.

#What is BLoC? <a name="What-is-BLoC?"></a>

**BLoC**, which stands for *Business Logic Component*, is a pattern created by *Paolo Soares* and *Cong Hui* from Google that aims to separate the business logic from the view in a given application. One of its primary goals is to make the business logic **reusable** and independent from the corresponding view. Initially, this pattern was introduced within the [Flutter](https://flutter.dev/) community, but throughout this article, we will take the concepts of BLoC and implement them in a React application. To start with, BLoC has these main rules:

1. Each BLoC should have a `dispose()` method to cleanup any ongoing streams.

2. Any data coming in a BLoC or any change in state within a BLoC must be done through a method.

3. Any data coming out of a BLoC should be done through a stream/observable.


The following diagram illustrates how the view and a BLoC interacts with each other.

![BLoC view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ikqrf44emkocxyo9zo9.png)

#Creating a Todo app using BLoC/RxJS <a name="Creating-a-Todo"></a>

Now that we've gone briefly over the concepts of the BLoC pattern, we will attempt to implement it in an example todo react application.

##App initialization <a name="App-initialization"></a>

First, lets initialize a react typescript project using `vite`:

```bash
npm create vite@latest todo-bloc --template react-ts
```

this should create a folder called `todo-bloc`. Navigate to that folder and install the dependencies:

```bash
cd todo-bloc && npm install
```

once done, we'll proceed by installing the [RxJS](https://www.npmjs.com/package/rxjs) package and the [uuid](https://www.npmjs.com/package/uuid) package (we'll need it later):

```bash
npm install rxjs uuid
```

we can now run the app in dev mode:

```bash
npm run dev
```

##Folder structure <a name="Folder-structure"></a>

In our simple todo app, we will create the following sub directories inside the `src` directory:

```bash
src/
 ├───blocs/
 ├───components/
 ├───hooks/
 └───models/
```
- **blocs:** will encapsulate all of the reusable business logic components in our app.

- **components:** will govern the view (UI) react components.

- **hooks:** will hold all of our custom hooks.

- **models:** will contains the interfaces and abstract classes that are shared across the app.

##Creating the models <a name="Creating-the-models"></a>

To simulate a real world application, we will start first by creating a `Todo` interface, which all of the todo objects in our app will conform to. We'll create a `Todo.ts` file under the models directory and add the following interface:

![code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dzz30ehs81gjsgqvfjy.png)

- `id`: to distinguish between different todo objects.
- `text`: the actual content of the todo object.
- `isDone`: to keep track of whether this specific todo is marked as done or not.

Secondly, we'll create the base `bloc` interface which will include all common methods/properties shared between the BLoC's in our app. Ultimately, we'll only need a `dispose()` method on the `bloc` interface as previously mentioned. We'll proceed by creating a `Bloc.ts` file under the models directory and adding the following interface:

![bloc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9kei0ntty48ne8y8noy.png)
 
- `dispose()`: a method invoked to close any opened streams and complete all observables within our BLoC.

##Creating the BLoC's <a name="Creating-the-BLoC's"></a>

Now that we have the interfaces in place, lets start by defining our business requirements for our app. Essentially, our app should:

- display todo items chronologically.
- provide a way to add a todo item.
- provide a way to mark todo items as finished.

First off, we'll create a `TodoBloc` class that will implement the `Bloc` interface that was previously imposed. Ultimately, this class will live in a file called `TodoBloc.ts` under the blocs directory. To implement the first business requirement, we'll have to find a way to keep track of the current list of todo items. This is where `Subject` from `RxJS` comes into play. To recall, any data coming out of a BLoC should be through a stream/observable. To achieve that, we will create a `BehaviourSubject` that will hold the most recent list of todos, and emit new items whenever the state changes:

![todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oq0h6pqg7zgsw8pq3swu.png)

- `_todos$`: is a private property of type `BehaviorSubject<Todo[]>` that will be responsible for holding our `Todo` objects.

- `dispose()`: we close/cleanup the `_todos$` subject in the previously defined dispose method.

you might now be wondering, how are we going to expose the `_todos$` subject to the view if the field is marked as private? good question. This is a bit tricky, because we don't want to expose the whole `Subject` to the view as we don't want the view to be emitting data directly to the Subject (we have to do it through a method as previously mentioned). Instead, we will create a getter for the `_todos$` field that will **only** expose the `Observable` side of our subject:

![getter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fx8haliwhrlrcv6q92i5.png)

To cover the second business requirement, we will create a method called `addTodo`, which will be responsible for taking in a `Todo` object and adding it to the `_todos$` subject:

![addTodo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gto6eanuy29aquii45m.png)

**Explanation:** the previous method accepts a `Todo` object as a param, gets the current list of todos from the `_todos$` subject, concatenates the given todo to the list of todos, and finally pushes the new list of todos to the `_todos$` subject.

To implement the final business requirement, we will add a `toggleTodo` method, which will be responsible for marking the given `Todo` item as done/undone:

![toggle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1juxus7lxgdp04il6sd.png)

**Explanation:** `toggleTodo` accepts a `Todo` object as a param, gets the current list of todos from the `_todos$` subject, loops over the retrieved list and toggles the given `Todo` item by id, and finally pushes the new todo list to the `_todo$` subject.

The final `TodoBloc` class should look like this:

![Todo Bloc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/072i3ijt730q3tt5pd8e.png)

##Creating the hook <a name="Creating-the-hook"></a>

Now that we're done implementing our business logic, we need to start thinking of a way to connect our BLoC to the anticipated view in our app. We can facilitate this process by creating a custom hook that will have the following responsibilities:

- accept an `Observable` of a generic type as a param.

- consume the values coming from the `Observable` by subscribing to it when the components mounts.

- preserve the state of the most recent value from the `Observable` and update the state each time a new value comes in.

- cleanup the subscription when the component unmounts.

Having these points in mind, we'll create a `useSubscribe` hook under the hooks directory to fulfill these responsibilities:

![hook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rk27458a1o313qdxjt4p.png)

**Explanation:** `useSubscribe` accepts an `Observable` of a generic type as a param. A state is created to store the most recent value emitted from the given observable using the `useState` hook. We then use the `useEffect` hook to subscribe to the given observable on component mount and update the state with the most recent value. We also add a cleanup callback that will unsubscribe from the observable once the components unmounts. Finally, we return a copy of the state to the view to be able to effectively use it in display.

##Creating the view <a name="Creating-the-view"></a>

Now that we have everything in place, let us create the view to demonstrate how we can tie these all pieces together. First, we create a simple `TodoItem` component under our components directory to display a single todo item:

![todo view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nub86fe4q5bad6tmsncc.png)

**Explanation:** the `TodoItem` component accepts two params - `todo`, which is the given `Todo` object, and `onToggle` callback, which will be triggered when the user marks the items as done/undone. The component displays the text property of the given todo in a `li` tag, and style is rendered according to the `isDone` property.

Finally, in our `App.tsx` file, we will tie all the pieces together:


![view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gphqyqd7o5hrnv41tb6s.png)

**Explanation:** we create an instance of the `TodoBloc` outside of our `App` component. We then consume the most recent value from the the `TodoBloc` by using the previously defined `useSubscribe` hook and passing the `todos$` observable as a param. Now, the `todo` variable will always hold the most recent list of todos, and will update accordingly. To give the user the ability to add new todo items, we create a simple form with a single controlled `input` element and a submit button. On submit, the `addTodo` method on the `TodoBloc` will be invoked, and a new `Todo` object will be pushed to the list of todos. Finally, to display the list of todo items, we map over the todos list and render a `TodoItem` component for each given todo. Each `TodoItem` will be provided an `onToggle` callback that will invoke the `toggleTodo` method on the `TodoBloc` for each item, respectively.

The final result will look/behave as follows:

![demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ly32ayd8khrwi2m7n0ol.gif)

##Considerations and closing remarks <a name="Considerations-and-closing remarks"></a>

As your application grows, your number of BLoC's will grow respectively and you'll need to find a way to manage them cleanly. Multiple view components may consume a single BLoC and you'll need to find a way to provide it to these components. One way to go about that is to use the [context api](https://beta.reactjs.org/apis/react/useContext) to provide a single bloc to multiple components. You may also want to look into the [Singleton design pattern](https://refactoring.guru/design-patterns/singleton) to preserve a single copy of a given BLoC within your app. You would also want to manage the disposal of each BLoC when it is no longer needed in your app (in our todo app, we did not call `dispose()` anywhere because the `TodoBloc` has the same lifespan as our application). Nevertheless, be flexible and reasonable with your design and do not over engineer your solutions. Happy coding 🎉!