Modern UI Frameworks: Components and Abstraction

30 Jan 2023
Article main image
Photo by Michal Matlon on Unsplash
  • # Abstraction

We build things—objects and concepts—by piecing parts together. We start with simple things, which we piece together in different ways to form complex things. Then we piece together these complex things to form a new layer of more complex things and so on until we reach our goal.

In this process, as complexity grows, we identify logical subsystems or categories and, for easier reference and communication, we name them. Then we inadvertently (or sometimes by design) start to think of each of these subsystems or categories, not as a certain arrangement of parts but as a simple thing, a unit. For example, the arrangement of simple parts responsible for executing software programs in a computer, we call a “processor”. And we think of the processor not as the super complex thing it is but as a simple thing - the thing that executes software programs in a computer.

This simple thing—formed by the mind from an arrangement of simple parts—is an abstract idea. That is, it’s not a particular arrangement of simple parts, it’s a sort of arrangement of simple parts. It is independent of a particular arrangement of simple parts. Varying arrangements can all be valid representations of an abstract idea as long they are of a certain sort. This is why the Intel 8085 and the Core i7 are both processors.

The formation of abstract ideas or abstraction is invaluable because it reduces the complexity we must deal with when building things. When we think in terms of abstract ideas, we ignore a subsystem’s arrangement of simple parts or, in software terms, its implementation. We only think about what the subsystem is or what it does, for example, a processor - a thing that executes software programs in a computer, not the super complex arrangement of simple parts that it is. Also, building the next layer will always be piecing together simple parts, for example, piecing together a processor, a RAM stick, a hard drive, etc to form a computer.

Abstraction in Programming

Abstraction has been there in programming for a long time. Strings, lists, graphs, and files are abstract ideas. They have no material existence. But they do have representations in the form of arrangements of simple parts, both software implementation and hardware (bits, registers, disks, etc). These representations can be many, varying in software implementation and the hardware the software runs on.

Because of abstraction, when we use a string data type, for example, we can ignore the immense complexity involved in the representation and manipulation of strings by a computer. And when we use GUI libraries we can ignore the immense complexity involved in rendering text, buttons, input fields and animation on an LCD screen.

Similarly, within an application, we can use business logic while ignoring its nuances by defining its implementation, putting it in a function or class and giving it a descriptive name like CheckoutService. The same is true for data retrieval and persistence logic (eg. FeedRepository) and anything else whose complexity becomes a nuisance.

When we can ignore the immense complexity of software and the computer, we can focus or specialise on what is relevant at the moment and so become more efficient and improve software quality.

Components and Abstraction

Modern UI frameworks like React, Vue and Jetpack Compose are component-based. That is, you build a UI using them by building components and then piecing these components together in a hierarchy with the final website or mobile app UI as the root.

A component is an arrangement of simple (and complex) parts that is given a name and then used and conceptualized as an abstract entity.

For example, this navigation bar component is a complex arrangement of divs, imgs and anchor elements (simple parts) and Disclosure, Image and ActiveLink (complex parts).

import { Disclosure } from "@headlessui/react";
import Image from "next/image";
import ActiveLink from "./ActiveLink";

export default function Nav() {
  const navigation = [
    { name: "Home", href: "/" },
    { name: "Events", href: "/events" },
    { name: "Blog", href: "/blog" },
    { name: "Team", href: "/team" },
    { name: "Be a partner", href: "/be-a-partner" },
    { name: "Be a member", href: "/be-a-member" },
  ];

  function classNames(...classes: any[]) {
    return classes.filter(Boolean).join(" ");
  }

  return (
    <Disclosure as="nav">
      {({ open }) => (
        <>
          <style jsx>{`
            .active {
              text-decoration: underline;
              color: #b70569;
            }
          `}</style>
          <div className="relative z-50 w-full flex-none text-sm font-semibold leading-6 text-slate-900">
            <div className="mx-auto max-w-container px-4 sm:px-6 lg:px-8s">
              <div className="absolute insent-y-0 right-0 sm:hidden">
                {/* Mobile menu button*/}
                <Disclosure.Button className="inline-flex p-3 my-1">
                  <span className="sr-only">menu</span>
                  {/* Mobile menu button*/}
                  <Image
                    src={open ? "/images/close.svg" : "/images/menu.svg"}
                    width={32.5}
                    height={19.5}
                  />
                </Disclosure.Button>
              </div>
              <div className="flex items-center justify-between py-[2.125rem]">
                <div className="flex-shrink-0 flex items-center">
                  <a className="mr-auto flex-none text-slate-900" href="/">
                    <span className="sr-only">She code Nairobi</span>
                    <img
                      className="block lg:hidden h-10 w-auto -my-5"
                      src="/images/logo.svg"
                      alt="she-code-nairobi"
                    />
                    <img
                      className="hidden lg:block h-21 w-auto"
                      src="/images/logo.svg"
                      alt="she-code-nairobi"
                    />
                  </a>
                </div>
                <div className="hidden sm:flex sm:items-center">
                  <div className="">
                    {navigation.slice(0, 4).map((link) => (
                      <ActiveLink key={link.name} activeClassName="active" href={link.href}>
                        <a
                          className="px-3 font-normal text-base"
                        >
                          {link.name}
                        </a>
                      </ActiveLink>
                    ))}
                  </div>
                </div>
                <div className="hidden sm:flex sm:items-center">
                  <ActiveLink activeClassName="" href="/be-a-partner">
                    <a className="inline-flex justify-center font-normal rounded-full py-3 px-3 bg-primary text-white my-2.5 w-52 h-13">
                      <span>Be a partner</span>
                    </a>
                  </ActiveLink>
                </div>
              </div>
            </div>
          </div>

          <Disclosure.Panel className="sm:hidden h-screen">
            <div className="flex flex-col items-center px-2 pt-2 pb-3">
              {navigation.map((link) => (
                <Disclosure.Button
                  key={link.name}
                  as="a"
                  href={link.href}
                  className={classNames(
                    link.name === "Be a partner"
                      ? "text-center text-md text-primary border border-primary rounded-full p-3 w-44 h-15"
                      : link.name === "Be a member"
                        ? "text-center text-md text-white  rounded-full p-3 bg-primary w-44 h-15 my-8"
                        : "flex text-primary py-4 text-2xl"
                  )}
                >
                  {link.name}
                </Disclosure.Button>
              ))}
            </div>
          </Disclosure.Panel>
        </>
      )}
    </Disclosure>
  );
}

But from the viewpoint of its usage, it’s a simple and abstract idea. It’s simply a navigation bar. And we ignore the complex arrangement of divs, as and the rest.

const Page = ({ children }: LayoutType) => {
  return (
    <>
      <Nav />
      <main>{children}</main>
      <Footer />
    </>
  );
};

Now building the next layer (in this case, a web page) is not piecing together the complex arrangement of parts that is the navigation bar and the complex arrangement of parts that is the footer. It’s piecing together a navigation bar and a footer.