coderain guide

From Zero to Hero: Learning React Frontend Basics

React has revolutionized the way we build web applications. Developed by Facebook (now Meta) in 2013, it has become the most popular JavaScript library for building user interfaces (UIs). What makes React stand out? Its component-based architecture, efficient rendering with the Virtual DOM, and seamless integration with other libraries/tools. Whether you’re a complete beginner or a developer looking to upskill, mastering React basics is a gateway to building dynamic, interactive, and scalable web apps. This blog will take you from "zero" (no React experience) to "hero" (confidently building basic React apps). We’ll break down core concepts with simple examples, hands-on projects, and clear explanations. By the end, you’ll understand React’s fundamentals and build your first functional app: a todo list!

Table of Contents

  1. What is React?
  2. Setting Up Your React Environment
  3. Understanding JSX: The Heart of React
  4. Components: The Building Blocks of React Apps
  5. Props: Passing Data Between Components
  6. State: Managing Dynamic Data
  7. Lifecycle Methods & Hooks: Controlling Component Behavior
  8. Routing with React Router
  9. Hands-On Project: Build a Todo App
  10. Conclusion
  11. References

What is React? {#what-is-react}

At its core, React is a JavaScript library for building user interfaces. Unlike full frameworks like Angular or Vue, React focuses solely on the “view” layer of the MVC (Model-View-Controller) architecture. This flexibility lets you integrate it with other libraries or existing projects.

Key Concepts of React:

  • Component-Based: UIs are built with reusable, independent “components” (e.g., buttons, forms, cards).
  • Declarative: Instead of writing step-by-step “how to” code (imperative), you describe “what” the UI should look like, and React handles the “how.”
  • Virtual DOM: A lightweight copy of the actual DOM. React updates the Virtual DOM first, then syncs changes to the real DOM (diffing), making apps fast.

Setting Up Your React Environment {#setting-up-your-react-environment}

To start coding React, you’ll need:

  • Node.js: A JavaScript runtime (includes npm, a package manager).
  • Code Editor:推荐 Visual Studio Code (VS Code) with React extensions (e.g., ESLint, Prettier).

Step 1: Install Node.js

Go to nodejs.org and download the LTS version. Verify installation by running these commands in your terminal:

node -v  # Should show a version (e.g., v18.17.0)  
npm -v   # Should show a version (e.g., 9.6.7)  

Step 2: Create a React App

Use create-react-app (a tool by Facebook) to bootstrap a new React project. Run:

npx create-react-app my-first-react-app  
  • npx (Node Package Runner) avoids installing create-react-app globally.
  • my-first-react-app is your project name (change it to something fun!).

Step 3: Run the App

Navigate into your project folder and start the development server:

cd my-first-react-app  
npm start  

Your app will open at http://localhost:3000 with a default React page.

Project Structure Explained

Let’s explore the key files/folders in my-first-react-app:

  • public/: Static files (HTML, images). The index.html is the entry point (React injects the app here).
  • src/: Your React code lives here.
    • index.js: Renders the root component (App.js) into the DOM.
    • App.js: The main component (you’ll edit this!).
  • package.json: Lists dependencies and scripts (e.g., npm start).

Understanding JSX: The Heart of React {#understanding-jsx}

JSX (JavaScript XML) is a syntax extension for JavaScript that lets you write HTML-like code in React. It’s not HTML, but a way to describe UI elements.

Why JSX?

Without JSX, creating React elements would require verbose code like:

React.createElement("h1", { className: "greeting" }, "Hello, React!");  

With JSX, it’s cleaner:

<h1 className="greeting">Hello, React!</h1>  

Key JSX Rules

  1. One Root Element: JSX expressions must have a single parent (use <div>, <React.Fragment>, or shorthand <>).

    // ❌ Invalid (two roots)  
    <h1>Hello</h1>  
    <p>World</p>  
    
    // ✅ Valid (single root with fragment)  
    <>  
      <h1>Hello</h1>  
      <p>World</p>  
    </>  
  2. Class vs. className: Use className instead of class (since class is a reserved word in JavaScript).

    <div className="box">This is a box</div>  
  3. Self-Closing Tags: Tags like <img>, <input>, or custom components must close with / (e.g., <img src="logo.png" />).

  4. Embed JavaScript: Use curly braces {} to inject variables or expressions into JSX.

    const name = "Alice";  
    <p>Hello, {name}!</p>  // Renders: "Hello, Alice!"  
  5. Conditional Rendering: Use ternary operators or logical && inside {}.

    const isLoggedIn = true;  
    <div>{isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p>}</div>  

Components: The Building Blocks of React Apps {#components-the-building-blocks-of-react-apps}

Components are reusable, self-contained pieces of UI. There are two types: Functional Components (modern, preferred) and Class Components (older, rarely used today).

Functional Components

A function that returns JSX. Simple, lightweight, and works with hooks (more on hooks later).

Example: A Greeting Component
Create a new file src/components/Greeting.js:

function Greeting() {  
  return <h1>Hello, React Learner!</h1>;  
}  

export default Greeting;  // Export to use in other files  

Import and use it in App.js:

import Greeting from './components/Greeting';  

function App() {  
  return (  
    <div className="App">  
      <Greeting />  {/* Renders the Greeting component */}  
    </div>  
  );  
}  

export default App;  

Class Components (Legacy)

Before hooks, components were classes extending React.Component. They use render() to return JSX.

Example (for reference only—use functional components!):

import React from 'react';  

class Greeting extends React.Component {  
  render() {  
    return <h1>Hello, React Learner!</h1>;  
  }  
}  

export default Greeting;  

Props: Passing Data Between Components {#props-passing-data-between-components}

Props (short for “properties”) let you pass data from a parent component to a child component. They are read-only (children can’t modify props).

Example: Passing a “Name” Prop

Update Greeting.js to accept a name prop:

function Greeting(props) {  
  return <h1>Hello, {props.name}!</h1>;  
}  

export default Greeting;  

In App.js, pass the name prop when using Greeting:

<Greeting name="Bob" />  {/* Renders: "Hello, Bob!" */}  
<Greeting name="Alice" />  {/* Renders: "Hello, Alice!" */}  

Destructuring Props

Simplify code by destructuring props directly in the function parameters:

function Greeting({ name }) {  // Destructure name from props  
  return <h1>Hello, {name}!</h1>;  
}  

Default Props

Set fallback values for props if none are passed:

function Greeting({ name = "Guest" }) {  // Default to "Guest"  
  return <h1>Hello, {name}!</h1>;  
}  

// Usage: <Greeting /> renders "Hello, Guest!"  

State: Managing Dynamic Data {#state-managing-dynamic-data}

State is data that changes over time (e.g., form inputs, counters, API data). Unlike props (passed from parents), state is managed internally by a component and can be updated.

Using useState Hook

Hooks are functions that let you use state and lifecycle features in functional components. useState is the most basic hook for state management.

Example: A Counter Component
Create src/components/Counter.js:

import { useState } from 'react';  // Import useState  

function Counter() {  
  // Initialize state: [currentState, setState] = useState(initialValue)  
  const [count, setCount] = useState(0);  

  return (  
    <div>  
      <p>You clicked {count} times</p>  
      <button onClick={() => setCount(count + 1)}>  
        Click me  
      </button>  
    </div>  
  );  
}  

export default Counter;  
  • count: The current state value (starts at 0).
  • setCount: A function to update count (never modify state directly with count++—always use the setter!).

State Updates Are Asynchronous

setCount doesn’t update count immediately. If you need to use the previous state, pass a function to setCount:

// ❌ Risky: count might not be updated yet  
<button onClick={() => setCount(count + 1)}>  

// ✅ Safe: uses the latest state  
<button onClick={() => setCount(prevCount => prevCount + 1)}>  

Form Input State Example

Track input values with state:

function NameInput() {  
  const [name, setName] = useState("");  

  return (  
    <div>  
      <input  
        type="text"  
        value={name}  // "Controlled component": value tied to state  
        onChange={(e) => setName(e.target.value)}  // Update state on input  
      />  
      <p>Hello, {name}!</p>  
    </div>  
  );  
}  

Lifecycle Methods & Hooks: Controlling Component Behavior {#lifecycle-methods—hooks-controlling-component-behavior}

Components have “lifecycle” events: mounting (created), updating (state/props change), and unmounting (removed). With class components, you used methods like componentDidMount(). With functional components, we use hooks like useEffect.

useEffect: Side Effects

useEffect lets you run code after render (e.g., data fetching, subscriptions, DOM updates). It replaces class lifecycle methods.

Syntax:

useEffect(() => {  
  // Code to run (side effect)  
  return () => {  
    // Cleanup code (runs before unmounting/updating)  
  };  
}, [dependencies]);  // Run effect only when dependencies change  

Example 1: Run on Mount (like componentDidMount)

Empty dependency array [] runs the effect once, after the component mounts:

useEffect(() => {  
  console.log("Component mounted!");  
}, []);  // Empty array = run once  

Example 2: Run on Update (like componentDidUpdate)

Include dependencies to run the effect when they change:

const [count, setCount] = useState(0);  

useEffect(() => {  
  console.log(`Count updated to ${count}`);  
}, [count]);  // Runs when count changes  

Example 3: Cleanup (like componentWillUnmount)

Return a cleanup function to avoid memory leaks (e.g., cancel subscriptions):

useEffect(() => {  
  const timer = setInterval(() => {  
    setCount(prev => prev + 1);  
  }, 1000);  

  return () => clearInterval(timer);  // Cleanup: stop timer on unmount  
}, []);  

Routing with React Router {#routing-with-react-router}

Single-Page Apps (SPAs) use client-side routing (no full page reloads). react-router-dom is the standard library for this.

Step 1: Install React Router

npm install react-router-dom  

Step 2: Basic Routing Setup

Update src/index.js to wrap your app with BrowserRouter (the root router):

import { BrowserRouter } from 'react-router-dom';  

ReactDOM.createRoot(document.getElementById('root')).render(  
  <BrowserRouter>  
    <App />  
  </BrowserRouter>  
);  

Step 3: Create Pages & Routes

Add three pages: Home, About, and Contact.

1. Create Page Components

  • src/pages/Home.js:

    function Home() {  
      return <h1>Home Page</h1>;  
    }  
    export default Home;  
  • src/pages/About.js and src/pages/Contact.js (similar structure).

2. Set Up Routes in App.js
Use Routes and Route to define which component renders at which URL:

import { Routes, Route, Link } from 'react-router-dom';  
import Home from './pages/Home';  
import About from './pages/About';  
import Contact from './pages/Contact';  

function App() {  
  return (  
    <div>  
      {/* Navigation Bar */}  
      <nav>  
        <Link to="/">Home</Link> |  
        <Link to="/about">About</Link> |  
        <Link to="/contact">Contact</Link>  
      </nav>  

      {/* Routes */}  
      <Routes>  
        <Route path="/" element={<Home />} />  {/* Home at / */}  
        <Route path="/about" element={<About />} />  {/* About at /about */}  
        <Route path="/contact" element={<Contact />} />  {/* Contact at /contact */}  
      </Routes>  
    </div>  
  );  
}  
  • Link: Replaces <a> tags to avoid full page reloads.
  • Route: path is the URL, element is the component to render.

Hands-On Project: Build a Todo App {#hands-on-project-build-a-todo-app}

Let’s combine everything to build a todo list app with:

  • Adding todos (form state).
  • Displaying todos (props).
  • Deleting todos (state updates).

Step 1: Project Setup

Create these files in src/components:

  • TodoForm.js: Input form to add todos.
  • TodoList.js: Displays all todos.
  • TodoItem.js: Individual todo item (with delete button).

Step 2: TodoForm.js

Handles input and adds todos to the list:

import { useState } from 'react';  

function TodoForm({ addTodo }) {  // addTodo is a prop from parent  
  const [todoText, setTodoText] = useState("");  

  const handleSubmit = (e) => {  
    e.preventDefault();  // Prevent default form submission  
    if (todoText.trim()) {  // Don't add empty todos  
      addTodo(todoText);  // Call parent's addTodo function  
      setTodoText("");  // Clear input  
    }  
  };  

  return (  
    <form onSubmit={handleSubmit}>  
      <input  
        type="text"  
        value={todoText}  
        onChange={(e) => setTodoText(e.target.value)}  
        placeholder="Add a new todo..."  
      />  
      <button type="submit">Add</button>  
    </form>  
  );  
}  

export default TodoForm;  

Step 3: TodoItem.js

Displays a single todo and a delete button:

function TodoItem({ todo, onDelete }) {  // todo (text) and onDelete (function) from parent  
  return (  
    <div>  
      <span>{todo}</span>  
      <button onClick={() => onDelete(todo)}>×</button>  
    </div>  
  );  
}  

export default TodoItem;  

Step 4: TodoList.js

Renders all todos by mapping over the todo list:

import TodoItem from './TodoItem';  

function TodoList({ todos, onDelete }) {  // todos array and onDelete from parent  
  return (  
    <div>  
      {todos.map((todo, index) => (  
        <TodoItem  
          key={index}  // Unique key for list items (use IDs in real apps)  
          todo={todo}  
          onDelete={onDelete}  
        />  
      ))}  
    </div>  
  );  
}  

export default TodoList;  

Step 5: App.js (Put It All Together)

Manage the todos state and pass functions/props to child components:

import { useState } from 'react';  
import TodoForm from './components/TodoForm';  
import TodoList from './components/TodoList';  

function App() {  
  const [todos, setTodos] = useState([]);  // todos is an empty array  

  // Add a new todo to the array  
  const addTodo = (text) => {  
    setTodos([...todos, text]);  // Spread existing todos and add new one  
  };  

  // Delete a todo by filtering it out  
  const deleteTodo = (textToDelete) => {  
    setTodos(todos.filter(todo => todo !== textToDelete));  
  };  

  return (  
    <div className="App">  
      <h1>Todo App</h1>  
      <TodoForm addTodo={addTodo} />  
      <TodoList todos={todos} onDelete={deleteTodo} />  
    </div>  
  );  
}  

export default App;  

Run the App!

Start the server with npm start. You can now add todos, see them listed, and delete them. Congrats—you built a React app!

Conclusion {#conclusion}

You’ve now mastered React basics:

  • JSX for writing UI.
  • Components and props for reusability.
  • State and useState for dynamic data.
  • useEffect for side effects.
  • Routing with React Router.
  • Building a todo app with all these concepts!

React is a journey—keep practicing by adding features to your todo app (e.g., edit todos, mark as complete) or building new projects (weather app, blog). The more you code, the more confident you’ll become.

References {#references}

Happy coding—you’re on your way to React hero status! 🚀