DEV Community

Lee Twito
Lee Twito

Posted on

Rediscovering Frontend Fundamentals

It's often said that skilled hands can achieve almost anything. However, recent observations in the industry have led me to believe that amidst the plethora of trends, paradigms, and novelties, we have lost sight of the core principles of frontend development.

In this piece, I will share several code snippets from a recent project and attempt to elucidate my thoughts on them. So without further ado, let's dive in!

Table of Contents

  1. The Labyrinth of Complexity
  2. Blunders of the Past
  3. The Double-edged Sword
  4. A Handful of Crucial Suggestions

The Labyrinth of Complexity

Consider this elementary CardComponent with an optional heading attribute. If this attribute is present, it is rendered within a wrapping div with a specific class.

const CardComponent = ({ content, heading }) => {
  return (
    <div className="card">
      {heading && <div className="card__heading">{heading}</div>}
      {content}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Simple scenarios work as intended. If the heading is dynamic and can return either content or null, our condition {heading && <div />} fails to recognize it and renders an empty div.

To tackle this issue, a developer came up with the following solution:

const CardComponent = ({ content, heading }) => {
  const headingRef = useRef();

  useEffect(() => {
    const containsContent = headingRef.current?.childNodes.length > 0;
    headingRef.current.style.display = containsContent ? "block" : "none";
  });

  return (
    <div className="card">
      {heading && (
        <div ref={headingRef} className="card__heading">
          {heading}
        </div>
      )}
      {content}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

However, this code only works on the initial render. If the footer updates asynchronously, useEffect won't be invoked. After much deliberation, developers decided to explore the MutationObserver.

When asked for advice, I offered a simpler solution that only required basic CSS:

.card__heading:empty {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

Developers often overcomplicate tasks without exploring basic CSS capabilities.

Blunders of the Past

In a previous project, we needed a side panel widget that would stretch to its full height without overlapping the header and footer. The basic formula looked like this: 100% - headerHeight - footerHeight.

The solution worked smoothly on all pages, except one. On that page, footerHeight was equal to 0. The developer who faced this bug discovered that document.querySelector('footer') returned null, even though the footer was still rendered on the page. They resorted to using MutationObserver.

Upon closer inspection, I found an alternative solution by simply rearranging a few lines of code:

<html>
<head></head>
<body>
  <header></header>
  <main id="root"></main>
  <script src="index.js"></script>
  <footer></footer>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Somehow, the <script /> tag appeared on the page before the footer. The <script /> tag is invoked synchronously, and the footer doesn't exist at that moment, so it's impossible to measure its height. By swapping the lines, everything started to work as intended.

Nowadays, developers rely heavily on modern tools like webpack-plugin, causing them to struggle when writing basic HTML.

The Double-edged Sword

React hooks are both a blessing and a curse. While they offer flexibility and an elegant way to handle state, they also significantly increase code complexity and the likelihood of making errors.

The main culprit is the useEffect hook. When the code is not carefully managed, it can easily result in infinite loops or multiple component renders.

To avoid such problems, it is crucial to learn the intricacies of hooks and pay attention to their dependencies.

A Handful of Crucial Suggestions

To help regain focus on frontend fundamentals, I offer the following suggestions:

  1. Take a step back and re-evaluate your knowledge of HTML, CSS, and JavaScript. Make an effort to master the basics before diving into advanced frameworks and libraries.
  2. Keep your code DRY (Don't Repeat Yourself) and follow the KISS (Keep It Simple, Stupid) principle. It will not only make your code more readable but also easier to maintain.
  3. Be cautious when using modern frameworks and tools. While they can be helpful, they may also introduce unnecessary complexity.
  4. Understand the pros and cons of the tools you use. This will allow you to make informed decisions and prevent issues down the road.

In conclusion, while frontend development continues to evolve, it is essential not to lose sight of the basics. By revisiting the core principles and focusing on simplicity, we can create more efficient, maintainable, and scalable solutions.

Top comments (1)

Collapse
 
brense profile image
Rense Bakker

I agree it's good to take a step back from time to time to look at what we're doing. It's true that vanilla CSS has grown a lot more powerful since the last time most people used it and sometimes just hiding an element might be enough.

I'm not sure I agree on the React hooks complexity example though... Most of the "complexity" with the useEffect hook came from people trying to use it as a replacement for the legacy React lifecycle methods. In it's essense its a pretty simple function:

useEffect(() => {
  // ... do "something"
  return () => {
    // cleanup any artifacts from "something"
  }
}, [dependency]) // execute provided callback each time the dependency changes
Enter fullscreen mode Exit fullscreen mode

The biggest complexity comes from the dependency array which can be empty (only execute on mount) or undefined (execute after each state change), which is what people tried to play with, to match lifecycle methods like componentDidMount and componentDidUpdate. I don't think you can blame the hook or the framework itself, for all the crap that people tried to put in it over the years :P