I'll be honest—when I first heard about React, I rolled my eyes. "Another JavaScript framework? Really?" I was perfectly fine with vanilla JavaScript and jQuery (yes, I know, I know). Why complicate things?
Then I tried building a slightly complex web app without React. A simple todo list that needed filtering, editing, and local storage. What started as "this should take an hour" turned into a nightmare of DOM manipulation, event listeners everywhere, and bugs that made me question my career choices.
That's when I gave React a shot. And honestly? It changed everything.
The Problem: Vanilla JavaScript Gets Messy Fast
Here's what my vanilla JS code looked like for a simple todo app:
// Adding a new todo
document.getElementById('add-btn').addEventListener('click', function() {
const input = document.getElementById('todo-input');
const todoText = input.value;
// Create elements
const li = document.createElement('li');
const span = document.createElement('span');
const deleteBtn = document.createElement('button');
// Set content
span.textContent = todoText;
deleteBtn.textContent = 'Delete';
// Add event listeners
deleteBtn.addEventListener('click', function() {
li.remove();
updateLocalStorage();
});
li.appendChild(span);
li.appendChild(deleteBtn);
document.getElementById('todo-list').appendChild(li);
updateLocalStorage();
input.value = '';
});
// And this is just for ADDING. Imagine editing, filtering, sorting...
Every feature meant:
- Manually creating DOM elements
- Attaching event listeners everywhere
- Keeping track of state across different functions
- Syncing everything with local storage
- Praying nothing breaks when I add a new feature
It was exhausting.
Enter React: Components Change Everything
Here's the same functionality in React:
function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
setTodos([...todos, { id: Date.now(), text: input }]);
setInput('');
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>
Delete
</button>
</li>
))}
</ul>
</div>
);
}
Look at that. Clean, readable, and everything in one place. No manual DOM manipulation. No scattered event listeners. Just data and how it should look.
What Actually Made My Life Easier
1. Components = Reusable Building Blocks
Instead of copy-pasting HTML and JS everywhere, I build once and reuse:
function Button({ onClick, children, variant = 'primary' }) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
}
// Now use it everywhere
<Button onClick={handleSave}>Save</Button>
<Button onClick={handleCancel} variant="secondary">Cancel</Button>
<Button onClick={handleDelete} variant="danger">Delete</Button>
One component, infinite uses. Change the Button component once, and it updates everywhere. That's powerful.
2. State Management That Makes Sense
Remember tracking variables across different functions in vanilla JS? React's useState hook solved that headache:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
State is local to the component. It updates automatically. The UI re-renders when needed. No manual DOM updates.
3. useEffect: Side Effects Without the Chaos
Need to fetch data? Save to local storage? Update the document title? useEffect handles it cleanly:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Fetch user data when userId changes
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]); // Only re-run when userId changes
if (loading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}
No more scattered AJAX calls. No wondering when to fetch data. The dependency array [userId] tells React exactly when to re-fetch. Beautiful.
4. Conditional Rendering is Intuitive
Vanilla JS:
if (isLoggedIn) {
document.getElementById('dashboard').style.display = 'block';
document.getElementById('login').style.display = 'none';
} else {
document.getElementById('dashboard').style.display = 'none';
document.getElementById('login').style.display = 'block';
}
React:
{isLoggedIn ? <Dashboard /> : <Login />}
One line. Clear. No DOM manipulation. Just logic.
5. Props Make Data Flow Obvious
Passing data from parent to child is explicit and traceable:
function App() {
const [theme, setTheme] = useState('dark');
return (
<div>
<Header theme={theme} />
<MainContent theme={theme} />
<Footer theme={theme} onThemeChange={setTheme} />
</div>
);
}
function Header({ theme }) {
return <header className={`header-${theme}`}>My App</header>;
}
I can see exactly where data comes from and where it goes. No more mystery bugs from global variables.
Real Project Example: My Experience
I recently built a movie search app for a class project. Here's how React made it painless:
The app needed:
- Search movies via API
- Display results in a grid
- Filter by genre
- Add to favorites
- Persist favorites in local storage
In vanilla JS, this would have been:
- 300+ lines of spaghetti code
- Multiple files trying to share state
- Event listeners attached and removed constantly
- localStorage syncing nightmares
With React:
function MovieApp() {
const [movies, setMovies] = useState([]);
const [favorites, setFavorites] = useState(
JSON.parse(localStorage.getItem('favorites')) || []
);
const [filter, setFilter] = useState('all');
// Auto-save favorites
useEffect(() => {
localStorage.setItem('favorites', JSON.stringify(favorites));
}, [favorites]);
const searchMovies = async (query) => {
const response = await fetch(`/api/search?q=${query}`);
const data = await response.json();
setMovies(data.results);
};
const toggleFavorite = (movie) => {
setFavorites(prev =>
prev.find(m => m.id === movie.id)
? prev.filter(m => m.id !== movie.id)
: [...prev, movie]
);
};
const filteredMovies = filter === 'favorites'
? favorites
: movies;
return (
<div>
<SearchBar onSearch={searchMovies} />
<FilterButtons filter={filter} setFilter={setFilter} />
<MovieGrid
movies={filteredMovies}
onToggleFavorite={toggleFavorite}
favorites={favorites}
/>
</div>
);
}
Clean components. Clear data flow. Everything in one place. I built this in a weekend instead of struggling for a week.
Things I Struggled With (And How I Got Past Them)
1. "Why isn't my component updating?"
Forgot that state updates are asynchronous. Solution: Use the functional form of setState:
// Wrong
setCount(count + 1);
setCount(count + 1); // Still adds only 1!
// Right
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Adds 2!
2. "Infinite re-render loop!"
Called setState directly in the component body. Solution: Always use useEffect or event handlers:
// Wrong - infinite loop
function Bad() {
const [count, setCount] = useState(0);
setCount(count + 1); // Re-renders forever!
return <div>{count}</div>;
}
// Right
function Good() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
}, []); // Runs once
return <div>{count}</div>;
}
3. "My list items aren't updating correctly"
Forgot to add keys. React needs them to track which items changed:
// Wrong
{items.map(item => <li>{item.name}</li>)}
// Right
{items.map(item => <li key={item.id}>{item.name}</li>)}
Is React Worth Learning?
For me? Absolutely. Here's why:
You should learn React if you:
- Want to build modern web applications
- Hate managing DOM manipulation manually
- Need to build reusable UI components
- Want a job in web development (it's everywhere in job postings)
- Like working with a massive ecosystem (tons of libraries, tools, community support)
React might not be for you if:
- You're building simple static sites (vanilla JS or even just HTML/CSS might be enough)
- You're overwhelmed by JavaScript already (get comfortable with JS first)
- You need SEO-heavy sites (though Next.js solves this)
My Advice for Learning React
1. Master JavaScript first
Seriously. Learn array methods (map, filter, reduce), destructuring, arrow functions, promises, and async/await. React will make SO much more sense.
2. Start with functional components and hooks
Ignore class components. They're legacy. Focus on useState, useEffect, and props.
3. Build projects, not tutorials
Watching tutorials feels productive but you don't really learn until you build. Start with:
- Todo app (classic for a reason)
- Weather app (API calls + useEffect)
- Movie search (combining multiple concepts)
4. Don't overthink state management early
You don't need Redux or Context API for your first projects. useState and prop drilling are fine while learning.
5. Read the official React docs
They're actually good. The new docs (react.dev) are beginner-friendly and well-written.
The Bottom Line
React didn't just make my code cleaner—it made web development actually enjoyable. Instead of fighting with DOM manipulation and spaghetti code, I spend time building features and solving interesting problems.
Is there a learning curve? Yes. Will you get frustrated with re-renders and hooks at first? Absolutely. But once it clicks, you'll wonder how you ever built web apps without it.
React turned me from someone who tolerated frontend development into someone who actually looks forward to it. And for a CS student trying to build a portfolio and land internships, that's been a game-changer.
What's your React journey been like? Still learning, or already building cool stuff? Would love to hear what projects you're working on! ⚛️
Top comments (0)