DEV Community

Liotti
Liotti

Posted on

The React Tunnel Vision

Are you tired of overcomplicating your React projects with excessive JavaScript? Well, so do I!

After exploring countless codebases, One thing I've noticed as a web developer is the tendency to get caught up in the React way of doing things, I've named this as the "React tunnel vision."

This phenomenon occurs when we become so focused on solving problems with React that we forget about other tools and solutions that may be better suited for certain tasks. This can lead to overcomplicating code, creating unnecessary abstractions, and sacrificing performance.

As someone who has fallen victim to this tunnel vision, I've learned the importance of taking a step back and considering other options, such as using plain CSS for certain styling tasks, or using vanilla JavaScript for simple interactions.

Whether you're a seasoned developer or just starting out, these tips can give you a real advantage and improve your project's performance.

Why does this happen?

This tunnel vision is not entirely our fault. Many times, we have tight deadlines and we simply do not have the luxury of experimenting with different approaches to solve layout problems. However, when we take a step back and consider using CSS, we can create more efficient and elegant solutions.

But that's not just it, for the sake of the readability and documentation of the codebase, having CSS being the only responsible of handling the styling and layouts can help a lot with the separation of concern.

As someone who has struggled with separating business logic from styling, I understand how frustrating it can be to have a tangled mess of code that leads to unnecessary re-renders. It's crucial to keep concerns separate to make code maintainable, easy to read, test, and modify.

In this post we are going to focus entirely on how CSS can help us solve many of the issues we tend to try solv

The mistakes we make along the way

As someone who has worked with React components and CSS styling, I understand the struggle of trying to separate business logic from styling logic within the same component. It's all too easy to fall into the trap of using state and conditionals to toggle CSS styles, leading to tangled and hard-to-maintain code.

One great example is the simple problem of accordions

Imagine you have this page that may contain from one to several accordions, if you have more than 1 accordion, the accordions should show closed, but if only one accordion is being display then open it, there is no need for it to be hidden

This would be the classic react way of solving this issue


import React, { useState } from 'react';
import Accordion from './Accordion';

const AccordionWrapper = ({ accordionItems }) => {
  const [openAccordionIndex, setOpenAccordionIndex] = useState(null);

  const handleAccordionClick = (index) => {
    if (openAccordionIndex === index) {
      setOpenAccordionIndex(null);
    } else {
      setOpenAccordionIndex(index);
    }
  };

  return (
    <div>
      {accordionItems.map((item, index) => (
        <Accordion
          key={index}
          title={item.title}
          content={item.content}
          isOpen={openAccordionIndex === index || (openAccordionIndex === null && index === 0)}
          handleClick={() => handleAccordionClick(index)}
        />
      ))}
    </div>
  );
};

export default AccordionWrapper;

Enter fullscreen mode Exit fullscreen mode

I've seen code like this through dozens of codebases, and it's okay, if you wrote solutions like this, chances are you this is not the code that is holding back your app from being blazingly fast and performant, but starting to identify and prevent this overuse of JavaScript can really improve your potential as a front end engineer

Now... How should you really write this component?


import React from 'react';

const Accordion = () => {
  return (
    <div>
      <input id="accordion" type="checkbox" name="accordion" defaultChecked />
      <label htmlFor="accordion">Accordion</label>
      <div className="accordion-content">
        {/* Accordion content here */}
      </div>
    </div>
  );
};

//And you can map this component without needing to pass any prop nor state!

export default Accordion;


Enter fullscreen mode Exit fullscreen mode

And simply use CSS


.accordion-wrapper {
  display: block;
}

input[type="checkbox"] {
  display: none;
}

label {
  display: block;
  padding: 10px;
  background-color: #eee;
  font-weight: bold;
  cursor: pointer;
}

input[type="checkbox"]:checked + label {
  background-color: #ccc;
}

.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.5s;
}

input[type="checkbox"]:checked ~ .accordion-content {
  max-height: 1000px;
  transition: max-height 0.5s;
}

/* :only-child selector to open the only accordion by default */
input[type="checkbox"]:only-child:checked ~ .accordion-content {
  max-height: 1000px;
  transition: max-height 0.5s;
}

Enter fullscreen mode Exit fullscreen mode
  • Accordion is represented by an input checkbox and a corresponding label
  • Label is styled like a button and toggles the checked state of the checkbox when clicked
  • Accordion content is a div element with the class "accordion-content"
  • Adjacent sibling selector (+) is used to style the label when checkbox is checked
  • General sibling selector (~) is used to style the accordion content when checkbox is checked
  • Max-height property controls height of accordion content
  • Transition property creates smooth animation when accordion is opened or closed
  • :only-child selector styles accordion content when it's the only child of the wrapper element
  • Checkbox state determines if the accordion is open or closed by default

So, as you can see, we could manage both the open/close states for each individual accordion and also the condition that if we only rendered a single accordion, said accordion should be open by default.

Little disclosure, I don't think you should handle every conditional rendering in react like this, obviously if you need to keep track of which accordions are open and then send that data over then a state is the obvious solution, but this is to prove that sometimes, you have to take a step back, try to get out of the react tunnel vision and reconsider if you can move some logic around and let CSS handle the rest.

The conclusion

There could be many arguments for writing the accordion in the React way, and I agree. However, I want to stress the importance of avoiding the React/JavaScript tunnel vision. Sometimes, it's worthwhile to take a step back and consider whether CSS already offers an efficient solution to a given problem. By doing so, you may be able to optimize your project's performance and make it more effective. In short, by being mindful of these considerations and striking a balance between the use of CSS and React, you can create effective, performant, and maintainable components anywhere you go.

Top comments (5)

Collapse
 
francisprovost profile image
Francis Provost

Using a checkbox to build an accordion isn't the most accessible way thorough . instead of using a checkbox and the checkedattribute, you can usearia-expended in your css and it is more precise for screen reader users. You still need some js to change the value though and the "react way" is probably a better solution if you use react and need to support "single open" accordion group

Collapse
 
brense profile image
Rense Bakker

You can also simply use a css-in-js solution like emotion/styled-components. The problem with the css solution is that you've just hidden all checkboxes in the entire app. Sure you can fix that, but its a common problem from the old days of having css code with unintended side effects that the other dev cant really find and so they add their own css rule with !important and before you know it, your accordion doesn't work anymore. CSS-in-js libraries take care of this problem for you, so your css rules never have an effect outside of the component they're intended for.

Collapse
 
ignacioliotti profile image
Liotti

I totally feel you on using CSS-in-JS. Personally, I think styled components are the way to go.

The CSS solution shown in the example was just for demonstration purposes; you could easily solve it by adding a class. But, what I really wanted to get across is that we often get too caught up in using JavaScript to solve everything when CSS can do the job just fine most of the time.

Collapse
 
brense profile image
Rense Bakker

I see, yes that is true, we don't always need JavaScript.

Collapse
 
joshuaamaju profile image
Joshua Amaju • Edited

Your reply is yet another example of this article. CSS Modules solves that