When React Starts Acting Like jQuery (and You Can Totally Tell 😅)

Published: (January 8, 2026 at 08:00 AM EST)
6 min read
Source: Dev.to

Source: Dev.to

Not a Fangirl – Just a Tool‑User

I’m not a fangirl of any single framework. For me, frameworks are tools.
Just like I prefer a good, sturdy hammer over a rusty one with holes, I also prefer tools that help me build software that’s maintainable and pleasant to work with. That’s why I like to stay up‑to‑date, understand how things work under the hood, and not just “use the thing everyone uses.”

I work with Angular on a daily basis — and yes, Angular forces you into a certain architecture (although believe me, you can still build absolute spaghetti in it… ask me how I know 😬).

But I genuinely love React. Whenever I do a side project, I very often reach for it. I adore JSX and how close React feels to plain JavaScript, especially compared to Angular. But… you know what they say: with great power comes great responsibility 😎. In the wrong hands that freedom can burn a project.

I’ve seen more than a few projects that weren’t just messy.
They looked like someone wrote them in jQuery, deleted $(document).ready, sprinkled some JSX over it… and genuinely tried to make it work 🤡.

  • Was it a blind migration from an old codebase?
  • Was it devs who, deep in their hearts, never really left jQuery behind? 😉

Hard to say. Either way – here are some classic signs your React code isn’t really React… it’s just jQuery wearing JSX. And yes, it shows – loudly.


1️⃣ One giant component that “does everything”

A React file (usually .tsx) that technically has sub‑components or a utility for CSS class names, but emotionally feels like an old‑school index.html with a giant <div> tag inside 😂.

Big blob: fetching data, updating UI, managing events, handling layout, maybe a modal, three dropdowns, a table, a sidebar… because why not?

🧨 Example

function App() {
  const [data, setData] = useState([]);
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [filter, setFilter] = useState("");

  useEffect(() => {
    fetch("/api/items")
      .then(res => res.json())
      .then(setData);

    // Direct DOM manipulation – a red flag
    document.getElementById("loader")?.style.display = "none";
    document.getElementById("list")?.style.display = "block";

    const el = document.getElementById("filter");
    el?.addEventListener("input", e => setFilter(e.target.value));
  }, []);

  return (
    <>
      {/* Loading... */}
      {data
        .filter(d => d.includes(filter))
        .map(d => (
          <div key={d}>- {d}</div>
        ))}

      <button onClick={() => setSidebarOpen(!sidebarOpen)}>Toggle</button>
      {sidebarOpen && "Hello"}
    </>
  );
}

🤔 Why does this happen?

  • Migrated from jQuery and shoved everything into one file.
  • “It works, don’t touch it.”
  • No early architecture decisions.
  • “We’ll refactor later” (we all know how that story ends).

✅ How it should be

  • Split components by responsibility (Single Responsibility Principle).
    • Data / logic components.
    • Presentation components.
  • Move reusable logic to custom hooks when appropriate.
  • Avoid direct DOM manipulation – let React own the UI.
  • Remember: lots of small components > one god‑component.

2️⃣ One giant useEffect that does… everything

useEffect(() => {
  // 🤹‍♂️ everything happens here
}, []);

Fetching data, adding event listeners, toggling classes, updating the DOM, talking to multiple services, scrolling, analytics, toast notifications… all in one glorious effect.

Basically: Take $(document).ready, replace it with useEffect, ship to prod 😎.

🧨 Example

useEffect(() => {
  fetch("/api/stats")
    .then(res => res.json())
    .then(data => {
      setStats(data);
      document.title = "Dashboard";
      const counter = document.getElementById("counter");
      if (counter) {
        counter.textContent = data.users;
      }
    });

  const resize = () => {
    document.body.classList.toggle("mobile", window.innerWidth < 768);
  };
  window.addEventListener("resize", resize);
  return () => window.removeEventListener("resize", resize);
}, []);

🤔 Why does this happen?

  • Lack of a mental model: “effect = place where magic happens”.
  • Copy‑paste from older code.
  • “Hey, it runs once, perfect place for everything!”
  • No understanding that effects should be focused and scoped.

✅ How it should be

  • Each effect should have one clear responsibility.
  • Split the giant useEffect into multiple focused effects.
  • Don’t put logic in effects if it can live in the render phase.
  • Remember: useEffect !== lifecycle method.
  • Avoid stuffing all app behavior into a single “runs once” effect.

3️⃣ useEffect for things that belong in JSX

This one hurts a little 😅. Doing DOM manipulation in an effect just to update text, class, visibility, or something that JSX can declare naturally.

Instead of:

if (condition) {
  // change DOM
}

React wants:

if (condition) {
  // render something else
}

🧨 Example (imperative)

useEffect(() => {
  const el = document.getElementById("message");
  if (error) {
    el?.classList.add("visible");
    el!.textContent = error;
  } else {
    el?.classList.remove("visible");
  }
}, [error]);

✅ Declarative JSX version

{error && <div className="visible">{error}</div>}

🤔 Why does this happen?

  • Still thinking imperatively: “UI is something I change, not something I describe”.
  • Old patterns: “Everything dynamic? → must go in useEffect!”.
  • Old habits die hard.

✅ How it should be

  • Express UI changes declaratively in JSX.
  • Show/hide elements using conditional rendering.
  • Derive classes from state instead of toggling them manually.
  • Think: “state changes → React re‑renders” not “state changes → I patch the DOM”.

TL;DR

  • Keep components small and focused.
  • Use custom hooks for reusable logic.
  • Reserve useEffect for side‑effects only (data fetching, subscriptions, etc.).
  • Let JSX describe the UI; avoid manual DOM manipulation.

When you follow these guidelines, your React code will feel like React again – not a jQuery‑styled Frankenstein. 🚀


“the DOM”

4️⃣ A giant “switch” on CSS classes instead of real logic

UI state stored… not in state
…not in props
…but… 🥁 in CSS class names.

The application logic becomes:

  • “if it has this class then it’s open”
  • “if it doesn’t then it’s closed”
  • “if it has this plus that then it’s in some magic state nobody fully understands anymore”

Congrats, you built a state machine… in your stylesheet 🙃

🧨 Example

useEffect(() => {
  const steps = document.querySelectorAll(".step");
  steps.forEach(step => {
    step.addEventListener("click", () => {
      steps.forEach(s => s.classList.remove("active"));
      step.classList.add("active");
    });
  });
}, []);
<button class="step">Enter fullscreen mode</button>
<button class="step">Exit fullscreen mode</button>

🤔 Why does this happen?

  • Legacy thinking.
  • “This used to work in jQuery, why change it?”
  • CSS was used as a state holder for years — the habit sticks.

✅ How it should be

  • Keep UI state in React state or a store.
  • Make UI derive from state instead of encoding logic in CSS.

For complex flows consider:

  • useReducer
  • a proper state machine

Rule of thumb

  • CSS = styling
  • React state = logic

5️⃣ Animations as “just toggle the class in JS”

Need animation?
Add/remove class in JS. Done.

React? Oh yes, still technically there 👀 just quietly watching.

No state. No declarative transitions. No structure.

Just:

click → add class → remove class later → hope nothing breaks

🧨 Example

function Notification() {
  useEffect(() => {
    const btn = document.getElementById("show");
    const box = document.getElementById("note");

    btn!.onclick = () => {
      box?.classList.add("visible");
      setTimeout(() => box?.classList.remove("visible"), 2000);
    };
  }, []);

  return (
    <>
      <button id="show">Show</button>
      <div id="note">Hello!</div>
    </>
  );
}
<button id="show">Enter fullscreen mode</button>
<button id="hide">Exit fullscreen mode</button>

🤔 Why does this happen?

  • Familiar old pattern.
  • Quick hack delivered to production.
  • Zero time allocated for frontend architecture.

✅ How it should be

  • Base animation triggers on React state, not manual DOM.

Simplest option

  • State → class toggle in JSX

Better options

  • CSS transitions driven by state
  • React Transition Group
  • Framer Motion

Avoid imperatively driving animations when you can declare them.


6️⃣ Keeping DOM elements inside state 😱

Rare… but I’ve seen it. And once you see it, you never really forget it — it leaves a mark 😅

const [el, setEl] = useState<HTMLElement | null>(null);

useEffect(() => {
  setEl(document.getElementById("target"));
}, []);

Further code omitted in the original source.

Back to Blog

Related posts

Read more »

What is React?

!Cover image for What is React?https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amaz...