MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

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

2026-01-08 21:00:12

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 😎 And 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”

React file. Looks like .tsx. Technically it has sub-components. Maybe even a utility for CSS class names. But emotionally?

Emotionally, it feels like an old-school index.html with a giant script tag living inside 😂

Big blob. Fetching data. Updating UI. Managing events. Managing layout. Oh, and probably a modal, three dropdowns, a table and a sidebar too, 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);

    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 (
    <>
      <input id="filter" />
      <div id="loader">Loading...</div>
      <ul id="list">
        {data.filter(d => d.includes(filter)).map(d => <li>{d}</li>)}
      </ul>
      <button onClick={() => setSidebarOpen(!sidebarOpen)}>Toggle</button>
      <div className="sidebar">{sidebarOpen && "Hello"}</div>
    </>
  );
}

🤔 Why does this happen?

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

✅ How it should be

  • split components by responsibility (Single Responsibility Principle)
  • separate:

    • data / logic components
    • presentation components
  • move 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 7 different 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";
      document.getElementById("counter").textContent = data.users;
    });

  const resize = () => document.body.classList.toggle("mobile", window.innerWidth < 700);
  window.addEventListener("resize", resize);

  window.scrollTo(0, 0);

  return () => window.removeEventListener("resize", resize);
}, []);

🤔 Why does this happen?

  • Lack of 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

  • every effect should have one clear responsibility
  • split giant useEffect into multiple focused effects
  • don’t put logic in effects if it can live in render
  • remember:

useEffect !== lifecycle method

  • avoid stuffing all app behavior into a single “runs once” effect

3️⃣ useEffect for things that should simply be 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

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

Meanwhile React:

{error && <p className="message">{error}</p>}

🤔 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

  • UI changes should be expressed declaratively in JSX
  • show/hide things using conditional rendering
  • derive classes from state, don’t toggle them manually
  • think:

    • “state changes → React rerenders”
    • not “state changes → I patch 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 means 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");
    });
  });
}, []);

🤔 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" className="notification">Hello!</div>
    </>
  );
}

🤔 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 😅

useState<HTMLDivElement | null>(null)

And then logic like:

  • “if we have this element, do things directly on it”
  • instead of storing intent in state.

That’s not React. That’s absolutely raw DOM puppet-mastering.

🧨 Example

const [el, setEl] = useState(null);

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

function highlight() {
  el.style.background = "yellow";
}

🤔 Why does this happen?

  • Confusing refs, state, and DOM.
  • Treating React as just a helper to render markup.
  • Not really understanding React’s role at all.

✅ How it should be

  • if you need to access DOM → use ref
  • store intent in state, not raw DOM nodes
  • DOM is implementation detail
  • state describes what you want to happen, not how to mutate the element

💬 Why this matters

I’m not saying code must always be perfect.
Sometimes quick hacks are fine. Sometimes deadlines win. Sometimes legacy constraints are real.

But when React is used like jQuery with JSX…

  • maintenance becomes nightmare
  • team onboarding becomes painful
  • bugs hide in dark corners nobody understands
  • architecture collapses under its own weight

React is powerful not because it manipulates DOM better.
It’s powerful because:
State → UI
not
🔧 UI → patch → patch → patch

🤍 A little reflection

React gives freedom. And that freedom is beautiful.
But freedom requires responsibility. Without it, React becomes:

a jQuery with better branding

And that’s a bit sad, because React can give us structure, clarity, predictability — if we let it.

If your project smells like the things above…
it’s not a shame.
It just means there is history, pressure, context, and human decisions behind it.

But maybe it also means…

it’s time to pause, breathe, and rethink a little 😊

🙋‍♀️ Your turn

I’m super curious:

👉 Have you seen “React in jQuery cosplay” in the wild?

Share your stories, scars, confessions, or survival tips!

Build Your Own Local AI Agent (Part 4): The PII Scrubber 🧼

2026-01-08 21:00:00

Welcome to the finale of our Local Agent series.

Processing sensitive user data (PII) is risky. You can't send it to the cloud. You don't want to read it manually.

The PII Scrubber Agent demonstrates a "Level 2" Agent capability: Writing its own tools.

The Goal

  1. Analyze a CSV (users.csv).
  2. Detect PII (Emails, Names).
  3. Write a Python script to scrub the data safely.
  4. Run the script to produce users_cleaned.csv.

The Architecture: "Plan, Code, Run"

Instead of rewriting the CSV line-by-line (slow and expensive), the Agent writes code to do it efficiently.

The Architecture:

The "Smarts" Requirement

This workflow crashed smaller models (llama3.2). They would try to run the script before writing it!

We upgraded to gpt-oss:20b, which successfully:

  1. Paused to inspect the file.
  2. Wrote the complete Python script.
  3. Executed it successfully.

Output of PII Scrubber

Conclusion

We've built 4 agents:

  • Tidy-Up: Simple Actions.
  • Analyst: DB Querying.
  • Archaeologist: File Editing.
  • Scrubber: Code Generation & Execution.

And we did it all locally. No API keys. No data leaks. Just you, Goose, and Ollama.

Complete code is available on Github here.

Go build something cool! 🪿

DTAP - super simple testing protocol for infrastructure audit

2026-01-08 20:54:15

DTAP is a new testing protocol allowing to test infrastructure with just a Bash scripts. Here is a quick example, let's test that /etc/dhcp/ directory and /etc/dhcpcd.conf file exists:

#!/bin/bash

session=$(date +%s)

ls /etc/dhcp/ 2>&1 | dtap --box - \
--session $session \
--params path=/etc/dhcp/ \
--check path-ok \
--desc "dhcp/ dir"

ls /etc/dhcpcd.conf  2>&1 | dtap --box - \
--session $session \
--params path=/etc/dhcpcd.conf \
--check path-ok \
--desc "dhcpcd.conf file"

dtap  --report  --session $session

The result will be:

DTAP report
session: 1767875428
===
dhcp/ dir ...... OK
dhcpcd.conf file ...... OK

Plain and simple

As one can see test scripts are just plain Bash commands, no fancy YAML or even high level programming languages.

Also DTAP follows WYSIWYG principle when we get exactly what we see, in a sense this something we would do trying to check existing of mentioned directory and file:

ls /etc/dhcp/
ls /etc/dhcpcd.conf

And if any errors occurred we will will get exactly what we are asking for - output of ls command which most of the Linux users probably are familiar with:

!/bin/bash
ls  /etc/does-not-exist 2>&1 | dtap --box - \
--session $session \
--params path=/etc/does-not-exist \
--check path-ok \
--desc "/etc/does-not-exist dir"

Output:

DTAP report
session: 1767875841
===
/etc/does-not-exist dir ...... FAIL
[report]
15:37:22 :: [sparrowtask] - run sparrow task .@path=/etc/does-not-exist
15:37:22 :: [sparrowtask] - run [.], thing: .@path=/etc/does-not-exist
[task run: task.bash - .]
[task stdout]
15:37:23 :: ls: cannot access '/etc/does-not-exist': No such file or directory
[task check]
stdout match <^^ "/etc/does-not-exist"  $$> False
=================
TASK CHECK FAIL
2

How this works?

There are two essentials primitives in DTAP:

  • boxes

  • and - checks

Box is an abstraction for everything we want to test - from web server to messages in syslog files. Boxes produces some output to be checked against check rules ( aka checks ). In the previous examples - boxe is just ls command which output redirected to a certain check rule via --box - notation ( meaning read box output from STDIN ). DTAP comes with some predefined boxes user can use them without writing any piece of code, but most of the time boxes - are something a user would write as a chain of Bash commands something likes that:

#!/bin/bash
( 
  2>&1
  cmd1;
  cmd2;
  cmd3;
  # ...
) | tap --box -

Or with a single script:

#!/bin/bash
. /some/box/script.sh | tap --box -

Checks are rules written on formal DSL and executed remotely on DTAP server, so users don't need to install anything, only small tap binary written on golang that interacts with a server send output from boxes to a server and get results back. Checks DSL is based on Raku regular expressions and is super flexible, allowing many things to do including extension by using many general programming languages. To list available checks just run:

tab --check_list

Follow double tap web site documentation to get details on using available checks.

It's easy to create new checks and add them to DTAP distribution, if you are instead please let me know. There is quick start introduction into check DSL could be found here

Conclusion.

DTAP is a new kid on the block, that allows to test infrastructure with using just a Bash yet very flexible and powerful. I encourage you to play with it, you can start with installation guide

Thanks for reading

Introducing HTTP Tracker Plus: Request Capture form Browser

2026-01-08 20:50:25

API inspectors and debuggers are essential tools for web application development. Being able to inspect API requests and responses makes debugging much easier and more effective.

I often use Chrome DevTools to check request and response data. I have also been using HTTP Tracker, but one important feature was missing: capturing the response body.Since HTTP Tracker is open source, I spent a long time exploring the codebase and trying to implement this feature, but I was not successful at first.

I finally found a solution by using Chrome Debugger Mode, which makes it possible to capture the response body. While working on this, I also improved the UI to make it clearer and easier to use.

The update includes:
Response body capture for selected requests only
UI improvements for better readability and usage

Demo GIF

I plan to publish this version to the Chrome Store and also submit a PR to the original project so others can benefit from it.
I have added a short GIF demo to show how it works.
GitHub repository (check the new branch): https://github.com/devded/http-tracker-plus
Feedback and suggestions are welcome.

I Built a Google Autocomplete Keyword Tool in Python (Source Code Included)

2026-01-08 20:44:24

KeySage - Keyword Suggester Pro

Most keyword tools are just thin wrappers around Google Autocomplete — but they hide the implementation behind subscriptions, APIs, and dashboards you don’t control.

I wanted something different:

A local desktop app

Written in plain Python

No API keys

No accounts

Full access to the source code

So I built KeySage, a Google Autocomplete keyword research tool using Tkinter, ttkbootstrap, threading, and requests — and I’m releasing both the EXE and the full source code.

Why Google Autocomplete?

Google Autocomplete is one of the most honest keyword datasets available.

Every suggestion:

Comes from real searches

Reflects user intent

Surfaces long-tail queries early

Under the hood, most SEO tools call the same endpoint:

https://suggestqueries.google.com/complete/search

KeySage connects to it directly — no API, no authentication.

High-Level Architecture

KeySage is a single-process desktop app with a background scraping thread.

Main components:

UI layer → Tkinter + ttkbootstrap

Networking → requests

Concurrency → threading + Event flags

Data model → sets + defaultdict

Exports → txt / csv / json

The goal was simplicity and hackability.

The Scraping Logic

Each base keyword goes through a configurable expansion pipeline:

Base keyword input

Optional prepend/append words

Character expansion:

a–z

a–z + 0–9

Google Autocomplete request

Deduplication

Live UI update

Example request:

params = {
    "client": "firefox",
    "q": query,
    "hl": language,
    "gl": country
}
requests.get(GOOGLE_SUGGEST_URL, params=params)

Results are streamed back to the UI using app.after() to keep Tkinter thread-safe.

Threading Without Freezing the UI

Tkinter is single-threaded, so scraping runs in a background thread.

Key patterns used:

threading.Thread(..., daemon=True)

threading.Event() for safe cancellation

UI updates marshaled back to the main loop

This allows:

Real-time keyword output

Instant cancellation

No UI hangs

Rate Limiting & Stability

Google doesn’t love aggressive scraping.

KeySage includes:

Built-in delays between requests

Optional proxy support

Stop-safe execution at every loop

This keeps the app usable without being reckless.

Automatic Keyword Clustering

After scraping, keywords are grouped by root word:

root = keyword.split()[0].lower()
clusters[root].append(keyword)

Simple? Yes.
Effective for content planning? Also yes.

Clusters are preserved across all export formats.

Exporting Data (Developer-Friendly)

Supported formats:

TXT → human-readable clusters

CSV → SEO tools & spreadsheets

JSON → automation & pipelines

Everything stays open and editable.

UI: Tkinter, But Not Ugly

The interface uses ttkbootstrap to avoid the “default Tkinter look”.

Features:

Responsive layout

Themed widgets

Collapsible advanced settings

Live scrolling output

Built-in help window

It’s intentionally minimal — no Electron, no web stack.

EXE vs Source Code

I’m offering three options:

Windows EXE ($7) — run instantly, no Python required

Full Source Code ($10) — learn, modify, extend

EXE + Source ($12) — best of both

👉 Get KeySage here:
https://matetools.gumroad.com/l/zwqcli

Why Release the Source Code?

Because developers learn best from real tools — not toy examples.

You can:

Add async requests

Improve clustering

Add new export formats

Integrate other suggestion sources

Turn it into a CLI or API

This is meant to be a foundation.

Final Thoughts

KeySage isn’t trying to replace enterprise SEO platforms.

It’s a transparent, hackable keyword tool built with:

Python

Common libraries

Simple architecture

No vendor lock-in

If you want a practical example of a real-world scraping + desktop app — or a tool you can actually use — this is for you.

👉 Buy / Download KeySage:
https://matetools.gumroad.com/l/zwqcli

Rethinking AI from First Principles

2026-01-08 20:37:10

Nasab — Lineage
A framework for building AI systems that learn like nature intended.

This is documented thinking. The framework emerged through conversation with multiple AI systems. Each contributed. Each critiqued. The full lineage is preserved. Nothing is hidden. Nothing is deleted.

Nasab: Rethinking AI from First Principles
Faith, Evolution, and the Crocodile Strategy

NASAB