Composable patterns in react #take-2

A functional approach to building react applications. How to make API calls better ? Here’s #take-1 if you have missed.

Gaurav Pasarkar
Geek Culture
6 min readJun 30, 2022

--

Unix pipes

The Unix philosophy, originated by Ken Thompson emphasizes building simple, short, clear, modular, and extensible code that can be easily maintained and repurposed by developers other than its creators. The Unix philosophy favors composability as opposed to monolithic design

So its not surprising that Douglas Mcllroy came up with the proposal of Unix pipelines. He thought like a mathematician. I believe this was the birth of functional programming (don’t quote me on that). The core building block of FP has always been composition. Now grep, awk and ps are all small programs which just do 1 thing. However, because of the way every program is designed in Unix, they can be composed to create much more complex programs. Look at the following snippet.

List the process IDs (PIDs) for all systemd-related processesps -ef | grep systemd | awk '{ print $2 }'

It creates a pipeline starting from ps -ef passes the output to grep systemd to find all systemd processes and finally awk '{ print $2 }' that prints the second column of the resulting table.

In #take-1 we explored different flavours of json-api-client. Now lets take it up a notch

User Story: Given i am on home page, when the page loads, users from an api should be shown in a table below

Now we already have a json-api-client. We also have a useJsonApi hook that can load data in state causing the page to re-render. To summarise what we had.

jsonApiClient

jsonApiClient

useJsonApi

useJsonApi

Can we build upon these 2 blocks. Yes we can :) Without further ado

useJsonApiOnLoad

useJsonApiOnLoad

If you are familiar with react, all we need to do for doing things on load is to wrap things inside useEfffect. And that's exactly what we did. The useJsonApiOnLoad hook takes in any hook that adheres to UseJsonApi contract. Under the hood, we need to memoize the api. This is useful for child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate). And once we have a memoized api, we wrap it inside useEffect. Easy peasy...

Note: Don’t worry too much about ApiResponse & Omit<T, keyof UseJsonApi>. We will get to that later.

The App

The App

And there’s more

Assumption: User is created asynchronously by some other process. So its possible the api would return a 404 while that operation is still in progress.
User Story: Given i am on home page, when the page loads, users from an api should be shown in a table below.

We can reuse all the flavours of json-api-client that we saw earlier.

The App

Assumption: We have some mechanism to get the access token for making api calls.
User Story: Given i am an authorised user on home page, when the page loads, users from an api should be shown in a table below.

We already have useJsonApi hook that deals with storing the api response in state. We also have useJsonApiOnLoad to make an api call on page load. All we need is a missing piece to add authorisation header when that api call is made. Lets create that missing piece

useAuthorisedJsonApi

useAuthorisedJsonApi

All we are doing inside the hook is using the accepting UseJsonApi payload and overriding the callApi implementation to add authorisation headers. These auth headers could be fetched using any mechanism

Now it’s getting interesting. Lets take a look at how we wire things up

The App

The App

If we wanted to make an authorised api call on a button click instead of page load, all we have to do is compose things we want. Just that the headers will be passed when callApi is invoked on button click.

The App

The App

And again we can use any flavour of jsonApiClient under the hood

The App

But how was all of this possible. Remember this ?

Note: Don’t worry too much about ApiResponse & Omit<T, keyof UseJsonApi>. We will get to that later.

Now look at the code for useJsonApiOnLoad again

useJsonApiOnLoad

For our contract, instead of relying solely on UseJsonApi contract, we relied on any specification that extends UseJsonApi. What this allowed us to do was to accept any other data that was passed in, and pass it back to the caller in the response. And we also made it type safe by saying that the response will contain ApiResponse and anything that was extra on UseJsonApi apart from the core contract :)

We are not stopping just here. Lets spice things up even more.

User Story: Given i am on home page, when the page loads, i want to show a loading indicator till the time data is fetched from the api.

We already have the core building blocks with us.. viz… jsonApiClient, useJsonApi and useJsonApiOnLoad. All we need is for someone to maintain a loading state. And then find a way to stitch it all together. Without further ado...

useJsonApiStates

useJsonApiStates

Again the code is extremely straight forward. We still rely on the core UseJsonApi contract. All we do is maintain a loading state locally and turn it on and off before and after the api call is made. That's it.

Note: This can be easily extended to error states as well. I will leave that to your imagination.

So far we haven’t created any abstractions for react components. Lets use this opportunity to introduce one

SuspenseOnLoad

SuspenseOnLoad

Once we have stitched the required hooks, all we do is subscribe to the loading state. If its loading, show the loading component. And when the data is present, show the data :D. I have purposefully kept the client as an input param. This allows us to pass any flavour of the client inside. Also there is absolutely nothing stopping us from adding useAuthorisedJsonApi to the mix.

The App

The App

And just to get closure.

User Story: Given i have a button Fetch, on the page, when i click on Fetch, i should see a loading spinner before the data is fetched from the api.

We can take all the inspiration we need from SuspenseOnLoad

SuspenseOnTrigger

SuspenseOnTrigger

As we are not relying on the page load event, we need a trigger component to start things for us. Apart from that the code should be self explanatory.

The App

The App

And that is a wrap. Its essentially the power of composition. It has enabled us to create new abstractions on the fly by composing existing one’s. The concept of composition has always been dear to my heart. Hope you folks find it useful. Cheers.

--

--

Gaurav Pasarkar
Geek Culture

I am a developer. Love to write code. Love to write about my experiences / opinions with tech and everything that goes with it