What Vanilla JS Concepts Should You Learn Before React?
Purpose
This post explains which JavaScript concepts you should learn before diving into React. I’ve seen many developers jump straight into React without understanding JavaScript fundamentals, and they struggle to understand why React code looks the way it does.
The Problem
When I tried to learn React directly without solid JavaScript fundamentals, I got confused by code like this:
const [count, setCount] = useState(0);
function Square({ value, onSquareClick }) { return <button onClick={onSquareClick}>{value}</button>;}
onSquareClick={() => handleClick(0)}I didn’t understand:
- What those square brackets were doing
- Why the curly braces appeared around the function parameters
- What that arrow function syntax meant
- How
onClickwas different fromonclickin vanilla JS
A Reddit discussion I found showed I wasn’t alone. One user said: “Most developers these days start and end with react and barely have a clue what’s going on under the hood.” Another commented: “Typically you learn HTML, you learn the vanilla DOM API, then you move on to a framework like React.”
What I Learned
After going back to learn vanilla JavaScript fundamentals, I realized React is just JavaScript with a specific way of writing it. Here’s what I think you need to know.
1. Modern JavaScript Syntax
React uses modern JavaScript syntax throughout. I found these concepts essential.
Arrow Functions:
const add = (a, b) => a + b;const greet = name => `Hello, ${name}`;
// Multi-line with explicit returnconst createButton = (text) => { const btn = document.createElement('button'); btn.textContent = text; return btn;};React uses arrow functions everywhere for event handlers:
<button onClick={() => handleClick(0)}>Click me</button>This arrow function is equivalent to: function() { return handleClick(0); }. I needed to understand this syntax to know why we wrap function calls in arrow functions for React event handlers.
Array Destructuring:
const numbers = [1, 2, 3];const [first, second] = numbers; // first=1, second=2const [head, ...tail] = numbers; // head=1, tail=[2,3]React’s useState hook relies on this pattern:
const [count, setCount] = useState(0);// count = current value// setCount = function to update itI struggled with this at first because I didn’t realize useState returns an array with two elements. The destructuring syntax just extracts them into named variables.
Array Methods:
React heavily uses map(), filter(), and slice().
// Map creates a new arrayconst numbers = [1, 2, 3];const doubled = numbers.map(n => n * 2); // [2, 4, 6]
// Filter keeps items that match a conditionconst evens = numbers.filter(n => n % 2 === 0); // [2]
// Slice creates a copyconst copy = numbers.slice(); // [1, 2, 3]React uses map() for rendering lists:
const moves = history.map((squares, move) => ( <li key={move}> <button onClick={() => jumpTo(move)}> Go to move #{move} </button> </li>));Object Destructuring:
const person = { name: 'Alice', age: 30 };const { name, age } = person; // name='Alice', age=30
// Rename propertiesconst { name: userName } = person; // userName='Alice'React uses this for props:
function Avatar({ person, size }) { return <img src={person.imageUrl} alt={person.name} />;}
// Without destructuring:function Avatar(props) { return <img src={props.person.imageUrl} alt={props.person.name} />;}2. DOM Manipulation Fundamentals
You don’t need to master the DOM API, but understanding these concepts helps.
Element Selection:
const button = document.querySelector('#myButton');const allButtons = document.querySelectorAll('button');const container = document.getElementById('container');Event Handling:
button.addEventListener('click', () => { console.log('Button clicked!');});
// With event objectbutton.addEventListener('click', (event) => { console.log('Clicked:', event.target.textContent);});React’s event system is similar but handles things differently (synthetic events):
<button onClick={(event) => { console.log('Clicked:', event.target.textContent);}}>Click me</button>Creating Elements:
const newDiv = document.createElement('div');newDiv.textContent = 'Hello';newDiv.className = 'greeting';document.body.appendChild(newDiv);React does this for you declaratively, but sometimes you still need direct DOM access with refs:
function Form() { const inputRef = useRef(null);
function handleClick() { inputRef.current.focus(); // Direct DOM API }
return ( <> <input ref={inputRef} /> <button onClick={handleClick}>Focus input</button> </> );}3. Immutability
React requires understanding why you don’t modify state directly.
const original = [1, 2, 3];original[0] = 99; // Direct mutationconsole.log(original); // [99, 2, 3]React requires creating copies:
const nextSquares = squares.slice();nextSquares[i] = 'X';setSquares(nextSquares);
// Wrong: Direct mutationsquares[i] = 'X'; // This won't trigger re-rendersetSquares(squares);I made this mistake early on and couldn’t figure out why my components weren’t updating. The key is: React needs to know when data changes, so you must create new objects/arrays instead of modifying existing ones.
4. Functions and Objects
React components are functions that return JSX.
function greet(name) { return `Hello, ${name}`;}
// Arrow function equivalentconst greet2 = (name) => `Hello, ${name}`;
// Object propertiesconst config = { apiUrl: 'https://api.example.com', timeout: 5000};
// Access propertiesconsole.log(config.apiUrl);React components follow this pattern:
function Button({ label, onClick }) { return ( <button onClick={onClick}> {label} </button> );}
// It's just a function that returns markup5. Asynchronous JavaScript
For data fetching in React, you need to understand promises.
fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));React uses useEffect for this:
function DataFetcher() { const [data, setData] = useState(null);
useEffect(() => { fetch('https://api.example.com/data') .then(response => response.json()) .then(setData) .catch(console.error); }, []);
return data ? <div>{data}</div> : <div>Loading...</div>;}Comparison: Vanilla JS vs React
Here’s how vanilla JS and React handle the same task.
Vanilla JS (imperative):
const button = document.querySelector('#myButton');button.addEventListener('click', () => { button.textContent = 'Clicked!'; button.style.backgroundColor = 'red';});
const list = document.querySelector('#list');data.forEach(item => { const li = document.createElement('li'); li.textContent = item; list.appendChild(li);});React (declarative):
function ButtonList() { const [clicked, setClicked] = useState(false);
return ( <> <button onClick={() => setClicked(true)} style={{ backgroundColor: clicked ? 'red' : 'gray' }} > {clicked ? 'Clicked!' : 'Click me'} </button> <ul> {data.map(item => <li key={item}>{item}</li>)} </ul> </> );}React lets you describe what you want, and it figures out how to update the DOM. But you still need to understand what’s happening under the hood.
Learning Path
Based on my experience, here’s the order I recommend:
HTML Basics (1-2 days) ↓JavaScript Fundamentals (2-3 weeks) - Variables, functions, objects, arrays - Modern syntax (arrow functions, destructuring) - Array methods (map, filter, slice, forEach) - DOM selection and manipulation basics - Promises and async/await ↓React Basics (2-3 weeks) - Components and JSX - Props and state - Event handling - Lists and keys ↓React Advanced (ongoing) - Hooks (useState, useEffect, useRef, useCallback) - State management - Performance optimizationWhy This Matters
When I learned these JavaScript concepts first, React became much easier to understand. I could recognize the patterns instead of being confused by the syntax.
Here’s what I think is most important:
-
Debugging becomes easier: React errors often mask JavaScript mistakes. When you understand vanilla JS, you can spot when the problem is React vs. basic JavaScript.
-
You understand the abstractions: React’s components are functions, props are parameters, state is data that changes. These are all JavaScript concepts.
-
Escape hatches make sense: React’s refs and effects require direct DOM knowledge. Without understanding the DOM, these features seem like magic.
-
Learning is faster: You’re not learning two things at once (React + JavaScript). You learn React patterns that build on what you already know.
What You Don’t Need to Master
I think you don’t need to become a vanilla JS expert before React. Focus on:
- Modern syntax (ES6+)
- Basic DOM concepts
- Array methods
- Functions and objects
- Basic async handling
Skip for now:
- Complex DOM manipulation (React handles this)
- Browser-specific APIs (unless you need them)
- Advanced patterns (learn these when needed)
Summary
In this post, I explained which vanilla JavaScript concepts you should learn before React: modern syntax (arrow functions, destructuring, array methods), basic DOM manipulation, functions and objects, and async handling. The key point is that React is just JavaScript with a specific way of writing it. Understanding the fundamentals makes React’s patterns clearer and helps you debug effectively. You don’t need to master vanilla JS, but learning these core concepts first will make your React journey much smoother.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 React Official Tutorial
- 👨💻 Modern JavaScript Tutorial (JavaScript.info)
- 👨💻 MDN JavaScript Guide
- 👨💻 Reddit Discussion: Vanilla JS before React
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments