I Was the Only User, So I Never Hit the Bug I Saw
It's been a while since my last post. I've been building connections, mostly commenting on other people's posts, because I noticed it did more for my views and impression on these platforms than I expected. But I've also been learning, so here's what I picked up this week.
What I learned, and how I made it stick
This week was data manipulation in React: searching, filtering, sorting and pagination. Following a tutorial is one thing. Building something with the concept is another, and building is what made it stick.
So I built a small Product Explorer with:
- category filtering
- text search
- sorting
- pagination
- derived state
- reset behavior when filters change
My brain went blank first
I had learnt the syntax and the concept. Then I was handed the exercise and my mind went blank.
The reason was simple: I hadn't broken the task into subtasks. "Build a Product Explorer" is not a task, it's a pile of tasks. Once I thought of it as a pipeline, I knew where to start:
MOCK_PRODUCTS → filter (category + search) → sort → paginate So I built it in that order: search logic first, then sorting, then pagination. Each stage takes the output of the one before it, which is why I could build them one at a time:
const productSearched = MOCK_PRODUCTS .filter((p) => selectedCategory === 'all' || p.category === selectedCategory) .filter((p) => p.title.toLowerCase().includes(searchTerm.toLowerCase())) const sortedProducts = [...productSearched].sort((a, b) => { /* ... */ }) const totalPages = Math.ceil(sortedProducts.length / productsPerPage) sortedProducts.slice(firstIndex, lastIndex).map(/* render cards */) Notice that nothing here is stored in state except the inputs (search term, category, sort option, current page). Everything else is derived on every render. That's the derived state idea, and it's why the app can't get out of sync with itself.
The bug I was stuck on
At some point my search stopped working. I traced the code and had two theories:
- Maybe I can't access
product.categorydirectly because it's an array of objects. - Maybe
productSearchedgets saved before anything is searched.
I killed the second one myself. The variable is recalculated on every render, and every keystroke triggers a render, so that couldn't be it. The first was wrong too: dot notation on objects inside .filter() is exactly right.
I was convinced the problem was in my search logic. It wasn't.
It was here:
const [selectedCategory, setSelectedCategory] = useState('') My dropdown's default option was value="all", and my filter checked selectedCategory === 'all'. On the first render, '' === 'all' is false, so every product was filtered out. Blank screen.
The bug was in an initial state value, nowhere near the logic I'd been staring at. I was looking in the right area of the code for the wrong kind of mistake.
The part I'm slightly embarrassed about
Here's the honest bit: I had actually noticed this blank screen before. I just didn't dig into it.
Why? Because I was the only user. I knew that picking a category from the dropdown made everything appear, so I just did that and moved on. It escaped my mind because it never got in my way.
That's the real lesson for me. When you're the developer, you only walk the paths you already know. You never see the bug a first-time user hits on their first second.
What my reviewers caught
I sent the code to my "teachers" (ChatGPT and Gemini 😂) and they pointed out edge cases I would not have found on my own:
- Wrong
totalPages. I calculated it fromMOCK_PRODUCTS.lengthinstead of the filtered results. Search for something with 2 matches and the pagination still thinks there are 3 pages. - Missing page reset. If you're on page 3 and then type a search that leaves 2 results,
currentPageis still 3 and the grid goes blank. The fix is to callsetCurrentPage(1)whenever search or category changes:
onChange={(e) => { setSearchTerm(e.target.value) setCurrentPage(1) }} - Missing
keyprop. I see this warning all the time and never treated it as serious. It is. React useskeyto track which item is which between renders. - A
.filter()that returnedproductornullinstead of a boolean. It worked, but only by accident of truthiness.
Same pattern as before: every one of these was invisible to me because I only tested the happy path.
What I'm taking away
1. Every part of the code matters. Warnings I scrolled past turned out to be real.
2. Test the unhappy paths on purpose. Being on page 3 and then searching. Starting the app and touching nothing. Those are the cases the developer skips and real users hit.
3. AI is useful for review, but it isn't a replacement for testing. It read my code and spotted problems fast, which is a real advantage. But for a real product, I'd still need actual testers and real users. What AI can do is make the review step cheaper.
4. Use AI for the boring parts, not the understanding. I didn't want to spend an hour fiddling with CSS layout and static HTML, so I prompted AI to generate the layout with all the React state, variables and event handlers stripped out and replaced with // TODO placeholders. The logic, which is what I was actually there to learn, I wrote myself.
Final thoughts
It's genuinely fun to learn this, because search, sort, filter and pagination show up in almost every application you use. And the thing that made it click wasn't the tutorial. It was getting blank-screened by my own code.
If you're learning React right now: build the thing, then try to break it like a stranger would.
What's the most embarrassing "I was the only user" bug you've shipped? 👇
原文:https://www.a8q9.cn/en/article/i-was-the-only-user-so-i-never-hit-the-bug-i-saw-c668e3.html