When React Starts Acting Like jQuery (and You Can Totally Tell 😅)
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
useEffectinto 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
useEffectfor 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.