Getting Started With React Study Guide For Examples
React is a JavaScript library for building user interfaces. It's not a framework. People argue about that distinction constantly, but the important part is that React only handles the view layer. You still need a router, state management, and a build tool if you want anything substantial running. I spent years building SPAs before React existed, and the mental shift was genuinely painful. You have to stop thinking about manipulating the DOM directly and start thinking in terms of declarative state. That alone trips up most people who come from jQuery or vanilla JS. The code feels wrong at first because it's doing less work, not more.
The Basics You Actually Need
Start with components. Everything in React is a component, whether it's a button or an entire page section. Functional components replaced class components as the standard way to write them. Class components still exist in production codebases everywhere, but if you're starting fresh, learn functional components with hooks from day one. Here's a component that does nothing exciting but demonstrates the structure:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
The useState hook is the most fundamental primitive you'll use. It takes an initial value and returns an array with the current state and a function to update it. The array destructuring syntax trips people up initially, but that's just JavaScript, not React specifically. Data flows down through props. A parent passes data to a child, and the child cannot modify that data directly. This is not a restriction React invented; it's intentional design to prevent bugs that come from scattered state mutations. Notice that UserProfile doesn't know where name and role came from. It just receives them. This separation becomes critical when components grow complex. If a prop depends on some calculation, do that calculation in the parent, not inside the child component.
Get the Full Details
The most frequent mistake beginners make is putting too much logic inside components. A component should handle rendering, not data fetching or complex calculations. When I audit code from people who just finished a React tutorial, roughly half the issues stem from this single problem. Another issue is unnecessary re-renders. React doesn't automatically optimize rendering for you. If you have a parent component and three child components, updating state in the parent will trigger a re-render of all three children unless you structure your code to avoid that. I encountered a specific problem recently with a form component that was re-rendering on every keystroke due to an inline object being passed as a prop. Here's what the problematic code looked like:
<Form onSubmit={{ handleSave(data) }} />
Creating an object literal inline means a new object is created on every render, which breaks reference equality checks. The fix was straightforward—wrap the function in useCallback or lift the handler into the parent scope: When rendering lists, always provide a stable key prop. Using array indices as keys works in simple cases but causes bugs whenever the list order changes. React uses keys to track which items have changed, and an index key makes that tracking unreliable after any sort or filter operation. If your data comes from an API and each item has a unique id, use that. Never use random values as keys. React will treat every item as new on each render and destroy/recreate the DOM unnecessarily.
useState works fine for local component state. Most components never need anything more complex than that. When you find yourself passing the same piece of state through five layers of components, that's called prop drilling, and it's a signal that you need either context or a state management library. The built-in useContext hook solves prop drilling without adding dependencies:

const ThemeContext = createContext('light');
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
<ThemeContext.Provider>
);
}
function Button() {
const { theme, setTheme } = useContext(ThemeContext);
return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Toggle</button>;
}
Context is not a replacement for a proper state management solution like Zustand or Redux Toolkit. It's designed for low-frequency updates like themes, language preferences, or authentication state. If you're dispatching hundreds of actions per second through context, you're using the wrong tool. Hooks follow strict rules: they must be called in the same order on every render, and they can only be called inside React function components or other hooks. Breaking these rules causes subtle bugs that are difficult to trace. The correct approach flattens the conditional:
Theoretical reading only gets you so far. Building something breaks things, and breaking things is where the actual learning happens. I recommend starting with a simple todo app, then moving to a dashboard with real data fetching, then attempting a project that requires client-side routing. For the routing part, React Router is the standard choice. Version 6 introduced a hook-based API that's cleaner than the old route-config approach:
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
<BrowserRouter>
);
}
Data fetching in React used to be handled awkwardly with componentDidMount in class components. The modern approach uses useEffect, though many teams now reach for libraries like TanStack Query to handle caching, retries, and refetching automatically. useEffect works for simple cases, but as your data requirements grow, manual effect management becomes a source of bugs. React provides React.memo to prevent unnecessary re-renders of components that receive the same props. It's a shallow comparison, so nested objects or functions in props can defeat it. Use it sparingly. Most components don't need it, and premature optimization here creates more problems than it solves. useMemo and useCallback serve similar purposes but for values and functions respectively. They cache results between renders when dependencies don't change. The common mistake is applying them everywhere because a tutorial said to. They add overhead too, and the benefit only shows up in components that re-render frequently with expensive computations.

Profile first. The React DevTools Profiler extension will tell you exactly which components are rendering unnecessarily. Don't guess.
What This Approach Won't Cover
This guide covers the core mechanics. It doesn't cover server-side rendering with Next.js, testing strategies, TypeScript integration, or deployment patterns. Those are separate topics that each have their own learning curve. If your goal is to build production applications, plan to spend additional time on testing and architecture beyond just learning the library syntax. Also worth noting: React's ecosystem moves fast. Patterns that were standard two years ago may not be the recommended approach today. Hooks replaced class components, Context API gained prominence, and the community largely moved away from Redux in favor of lighter solutions. Always check the documentation date when following tutorials, and prefer official docs over third-party guides for current best practices.