DEV Community

dbelokon
dbelokon

Posted on

Figuring out the Best Abstraction

Title: Figuring out the Best Abstraction

Welcome back! New Telescope version release report incoming!

Last Thursday, Telescope 2.7 was released, and with it, lots of cool stuff got in motion. Stuff like documentation, Supabase integration, parser service, the search service, react native front end were the areas that had a lot of activity by several Telescope team members, as well as new contributors 😋😋😋.

As for me, I had some discussion regarding the YouTube info banner and its implementation.

Thinking of the best abstraction

When I was trying to solve the issue, I decided to first read the code that handled the original GitHub information banner.

I learned there was a component called a ContextProvider. I wasn't sure what this was, so I read the React official documentation to understand what Context was supposed to be. After a few minutes of reading, I understood that there was a component that extracted the GitHub links from the original post, and that component shared the information necessary to other components via the use of a custom hook that used the useContext hook to access the information.

The main purpose for this structure is to not constantly pass the information necessary to child components via props, but instead let those child components access the necessary information by themselves 💡.

While the current structure worked just fine for the GitHub information, I noticed that there might be a little bit of an issue if we tried to incorporate the YouTube information banner through the same way.

A naive approach might be create a new provider that handles the YouTube information, so then you use the context in a new component.

The problem with this approach is that you would need to add another provider for the future Twitch info banner, so the approach won't scale 🙅🏻‍♀️🙅🏻‍♀️❌ if you add more types of banners.

Another approach might be instead generalizing the provider so that it can provide distinct types of information🤔. A good reason for this approach is that you won't have to modify the DOM structure every time you add a new provider.

This is where I started to think of the perfect abstraction…😌💡🌈

The GeneralInfoContext abstraction

When you have a general provider like this one, it works almost like a hub: some information will enter the provider, and it is the job of the provider figure out who asked for what information. The problem is figuring out how to write it so that it is:

  • simple,
  • easy to read,
  • easy to maintain,
  • localized in a single file.

When I discussed some of my ideas with the rest of the team, there were two opposite points:

  • we should have a single hook that provides the information because otherwise too many hooks would complicate the structure,
  • we should have several hooks that provide the information separately because otherwise a single hook would be too complex.

This is where I started to feel somewhat conflicted🤯🤪, since I wasn't sure how to keep discussing without having a small code sample for people to read and think about it.

The main idea was this: Create a GenericInfoContext, where it will provide a context that somehow had the information we wanted to share.

The interesting bit was achieving this, since I had two ways:

  • create an interface that would hold each group of information (that means, the interface has fields, where each field is the interface that groups related information such as GitHub information, YouTube information, etc).
  • create a component provider that would receive the type of information it would need to provide, as well as how to produce it.

If the second option sounds super abstract, let me be a little bit more specific.

Let's say that, in Typescript, I create a ContextBuilder, and this ContextBuilder expects a callback, where the callback has to expect an argument of type Post (which already exists in the code), and it returns an object of any type. So, something like this:


function buildContext(fnCallback: (p: Post) => any): Context {
    // do something with the callback
}

Enter fullscreen mode Exit fullscreen mode

If the idea still sounds complicated, don't worry, I didn't develop it that much because I thought it was getting out of hand😅😅.

However, I would like to explain why I was considering this in the first place. You see, the first approach would have something like this:

interface GenericInfoContextInterface {
    gitHubInfo: GitHubContextInterface
}
Enter fullscreen mode Exit fullscreen mode

If we had to add another group of information, like the YouTube information, we would need to do something like:

interface GenericInfoContextInterface {
    gitHubInfo: GitHubContextInterface;
    youTubeInfo: YouTubeContextInterface;
}
Enter fullscreen mode Exit fullscreen mode

And if we had to add the Twitch context, we would have to expand the GenericInfoContextInterface. The biggest problem I had with this approach is that it is not extensible without going through modification of the internals😵🤯, which is why I wanted to go with the second approach.

However, since this approach is simpler to understand and read, I decided to go with this one. You can view the PR that modifies the original context provider to work like this here.

Top comments (0)