- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Five React Mistakes in Forms and State That Cause the Weirdest Bugs
About Post
You delete the second row of a form, and the text from the third row vanishes instead. You type in an input and React prints a warning about "uncontrolled" components. A timer counts to 1 and then sits there forever, perfectly happy.
None of these are React bugs. They're the same handful of mistakes, made by beginners and by people with years of React behind them. Forms are where they show up most, because forms are where state changes on every keystroke.
Here are five of them, each as the symptom you'll actually see, the reason behind it, and the fix.
1. Copying state into more state
The symptom: a total, a filtered list or a "full name" that's one render behind, or out of sync after an edit.
Why: you stored something that can be calculated from other state, then tried to keep the two in sync with an effect.
// ❌ derived state, kept in sync by hand
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0));
}, [items]);
// ✅ just calculate it during render
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
The "fixed" version is shorter, always correct, and renders once instead of twice. If the calculation is genuinely expensive, wrap it in useMemo. Most aren't.
A close cousin is copying a prop into state: useState(user.name) only reads user.name on the first render. When the parent switches to a different user, the input keeps showing the old name. If you want an edit form that resets for each record, give it a key: <EditUser key={user.id} user={user} />. A new key means a fresh component with fresh state.
2. Using the index as a key in an editable list
The symptom: you remove a row and the wrong row's values disappear, or a row's local state (an open dropdown, a half-typed note) jumps to its neighbour.
Why: React uses the key to decide which component is which between renders. With key={index}, deleting row 2 means the old row 3 now has key 2, so React hands it row 2's internal state.
// ❌ keys move when the list changes
{rows.map((row, index) => (
<ChargeRow key={index} row={row} onRemove={() => remove(index)} />
))}
// ✅ a stable id created when the row is created
function addRow() {
setRows((rows) => [...rows, { id: crypto.randomUUID(), label: '', amount: '' }]);
}
{rows.map((row) => (
<ChargeRow key={row.id} row={row} onRemove={() => remove(row.id)} />
))}
Index keys are fine for a static list that never reorders or shrinks. The moment users can add, remove or sort rows, every row needs its own identity. Generate the id when the row is created, not inside map, or you'll get a new key on every render, which is even worse.
3. Stale closures
The symptom: a counter, interval or async callback that keeps using an old value.
Why: every render creates new functions, and each function "remembers" the state from the render it was created in. An effect that runs once captures the state from the first render, forever.
function SessionTimer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setSeconds(seconds + 1); // ❌ always 0 + 1
}, 1000);
return () => clearInterval(id);
}, []);
return <span>{seconds}s</span>;
}
The fix is the updater form, which receives the latest value instead of the captured one: setSeconds((s) => s + 1). The same thing explains why calling setCount(count + 1) twice in one click only adds one. Both calls read the same count.
Stale closures also hide in async code. A submit handler that awaits an API call and then reads form state is reading the state from when the click happened. Usually that's what you want. When it isn't, pass the values you need into the function explicitly, or read them from a ref.
4. Switching an input from uncontrolled to controlled
The symptom: the console warns that "a component is changing an uncontrolled input to be controlled", and sometimes the input refuses to clear.
Why: an input with value={undefined} is uncontrolled. When the value later becomes a string, React has to switch modes mid-life, which it warns about because the behaviour gets unpredictable.
// ❌ form.email is undefined on the first render
const [form, setForm] = useState({});
<input value={form.email} onChange={handleChange} />
// ✅ start every field with a real value
const [form, setForm] = useState({ email: '', phone: '' });
// ✅ and guard values that come from an API as null
<input value={form.phone ?? ''} onChange={handleChange} />
The API case catches people more often than the first one. Your database has a nullable phone column, the API returns null, and the input quietly flips modes. Normalise null to an empty string when you load the record into the form.
5. Too many useEffects
The symptom: extra renders, flickers, effects that trigger other effects, and bugs nobody can trace because the logic is spread across five hooks.
Why: effects are for syncing with something outside React (a subscription, a timer, a non-React widget). They're often used instead for things that should happen in an event handler or during render.
// ❌ an effect reacting to a flag set by a click
const [submitted, setSubmitted] = useState(false);
useEffect(() => {
if (submitted) saveContract(form);
}, [submitted]);
// ✅ the user clicked, so do it in the click handler
async function handleSubmit(e) {
e.preventDefault();
await saveContract(form);
}
A useful question for every effect: "Is this happening because the component appeared, or because the user did something?" If it's the second, it belongs in the handler. The React team wrote a whole page on this, You Might Not Need an Effect, and it's one of the best things in the docs.
The rule behind all five: keep the minimum state you need, calculate everything else, give list items a real identity, and put user-triggered logic in event handlers. Most "weird React behaviour" is one of those four being broken.
What about form libraries?
For a login form, plain state is fine. For a contract form with dozens of fields, nested rows and validation, a library like React Hook Form or Formik saves you from re-writing the same plumbing, and React 19's form actions with useActionState handle the "pending, error, done" cycle for submissions. They help a lot, but they don't remove the need to understand the five problems above. You'll still meet keys, closures and controlled inputs inside them.
A quick checklist before you ship a form
- Is any state just a calculation of other state? Delete it.
- Do editable lists use stable ids as keys?
- Do intervals and repeated updates use the updater form?
- Does every input start with a defined value, including
nulls from the API? - Could any effect be an event handler instead?
Which of these cost you the most time the first time you met it? For me, the index key bug wins, because the UI looks completely fine until someone deletes the middle row.

Be first to comment it...