Introduction to React

Learn the basics of building user interfaces with React, the popular JavaScript library.

#react

#components

#intermediate

#frontend

React is a powerful JavaScript library for building user interfaces, particularly single-page applications. Developed and maintained by Facebook (now Meta), React has become one of the most popular front-end libraries due to its efficiency, flexibility, and robust ecosystem.

Why React?

React offers several advantages that have contributed to its popularity:

  1. Component-Based: React uses reusable components that manage their own state, making it easier to build complex UIs
  2. Declarative Syntax: You describe what your UI should look like based on the current state, and React efficiently updates the DOM
  3. Virtual DOM: React’s virtual DOM minimizes DOM operations, resulting in better performance
  4. One-Way Data Flow: Makes your code more predictable and easier to debug
  5. Strong Community & Ecosystem: Extensive libraries, tools, and support

Setting Up Your First React Project

The easiest way to start with React is using Create React App, a command-line tool that sets up a new React project with a good default configuration:

# Install Create React App globally (if you haven't already)
npm install -g create-react-app

# Create a new React project
npx create-react-app my-react-app

# Navigate to the project folder
cd my-react-app

# Start the development server
npm start

This will create a new React application and open it in your browser at http://localhost:3000.

React Components

Components are the building blocks of React applications. A component is a JavaScript function or class that returns a React element (typically JSX) that represents a piece of the UI.

Functional Components

Modern React applications primarily use functional components, which are simpler and support hooks:

import React from "react";

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

export default Greeting;

Class Components

While less common in new code, you might encounter class components in existing projects:

import React, { Component } from "react";

class Greeting extends Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Greeting;

JSX: JavaScript + XML

JSX is a syntax extension for JavaScript that looks similar to HTML. It allows you to write HTML-like code in your JavaScript:

const element = <h1>Hello, world!</h1>;

JSX allows you to embed JavaScript expressions using curly braces:

const name = "John";
const element = <h1>Hello, {name}!</h1>;

Important JSX rules to remember:

  • JSX elements must have a single root element
  • All tags must be closed (either with a closing tag or self-closing)
  • Attributes use camelCase (e.g., className instead of class)
  • JavaScript expressions go inside curly braces {}

Props: Passing Data to Components

Props (short for properties) are a way to pass data from parent to child components:

// Parent component
function App() {
  return <Greeting name="John" age={25} />;
}

// Child component
function Greeting(props) {
  return (
    <div>
      <h1>Hello, {props.name}!</h1>
      <p>You are {props.age} years old.</p>
    </div>
  );
}

State: Managing Component Data

State allows React components to change their output over time in response to user actions, network responses, or anything else.

Using State with Hooks

The useState hook lets you add state to functional components:

import React, { useState } from "react";

function Counter() {
  // Declare a state variable named 'count' with initial value 0
  const [count, setCount] = useState(0);

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

Each call to useState declares a separate state variable. You can use as many state variables as you need:

function UserForm() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
  const [email, setEmail] = useState("");

  // ...
}

Event Handling in React

React events are named using camelCase and passed as functions:

function Button() {
  function handleClick() {
    alert("Button was clicked!");
  }

  return <button onClick={handleClick}>Click me</button>;
}

You can pass parameters to event handlers using arrow functions:

function ItemList() {
  const items = ["Apple", "Banana", "Cherry"];

  function handleItemClick(item) {
    alert(`You clicked ${item}`);
  }

  return (
    <ul>
      {items.map((item, index) => (
        <li key={index} onClick={() => handleItemClick(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

Conditional Rendering

In React, you can conditionally render components or elements:

function UserGreeting(props) {
  const { isLoggedIn } = props;

  // Using ternary operator
  return <h1>{isLoggedIn ? "Welcome back!" : "Please sign in."}</h1>;

  // Alternative: using &&
  // return isLoggedIn && <h1>Welcome back!</h1>;
}

Rendering Lists

You can render lists of items using the map() function:

function TodoList() {
  const todos = [
    { id: 1, text: "Learn React" },
    { id: 2, text: "Build an app" },
    { id: 3, text: "Deploy to production" },
  ];

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

The key prop is important for helping React identify which items have changed. It should be unique among siblings and stable across renders.

Component Lifecycle with Hooks

The Effect Hook (useEffect) lets you perform side effects in functional components:

import React, { useState, useEffect } from "react";

function Clock() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    // This runs after every render
    const timerID = setInterval(() => {
      setTime(new Date());
    }, 1000);

    // Cleanup function that runs before the component unmounts or re-renders
    return () => {
      clearInterval(timerID);
    };
  }, []); // Empty dependency array means this effect runs only once after initial render

  return (
    <div>
      <h2>It is {time.toLocaleTimeString()}.</h2>
    </div>
  );
}

The dependency array (second argument to useEffect) controls when the effect runs:

  • Empty array ([]): Run once after initial render
  • With dependencies ([dep1, dep2]): Run when any dependency changes
  • No array: Run after every render

Forms in React

Working with forms requires managing the form state:

import React, { useState } from "react";

function SimpleForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
  });

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData({
      ...formData,
      [name]: value,
    });
  }

  function handleSubmit(event) {
    event.preventDefault();
    console.log("Form submitted:", formData);
    // Submit to an API, etc.
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">Name:</label>
        <input
          type="text"
          id="name"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
      </div>
      <div>
        <label htmlFor="email">Email:</label>
        <input
          type="email"
          id="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}
B[Create Static Components]
B --> C[Identify State Location]
C --> D[Add Interactivity with State & Props]
D --> E[Add Side Effects]
E --> F[Optimize Performance]
F --> G[Deploy]

“ —>

Building a Simple React App

Let’s put everything together in a simple Todo application:

import React, { useState } from "react";
import "./App.css";

function App() {
  // State for the todo list and new todo input
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState("");

  // Add a new todo
  function handleAddTodo(event) {
    event.preventDefault();
    if (newTodo.trim() === "") return;

    setTodos([...todos, { id: Date.now(), text: newTodo, completed: false }]);
    setNewTodo("");
  }

  // Toggle todo completion status
  function handleToggleTodo(id) {
    setTodos(
      todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }

  // Delete a todo
  function handleDeleteTodo(id) {
    setTodos(todos.filter((todo) => todo.id !== id));
  }

  return (
    <div className="app">
      <h1>Todo List</h1>

      <form onSubmit={handleAddTodo}>
        <input
          type="text"
          value={newTodo}
          onChange={(e) => setNewTodo(e.target.value)}
          placeholder="Add a new todo..."
        />
        <button type="submit">Add</button>
      </form>

      <ul className="todo-list">
        {todos.length === 0 ? (
          <li className="empty-message">No todos yet! Add one above.</li>
        ) : (
          todos.map((todo) => (
            <li key={todo.id} className={todo.completed ? "completed" : ""}>
              <span onClick={() => handleToggleTodo(todo.id)}>{todo.text}</span>
              <button onClick={() => handleDeleteTodo(todo.id)}>Delete</button>
            </li>
          ))
        )}
      </ul>
    </div>
  );
}

export default App;

Here’s some basic CSS to style this application:

.app {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
  font-family: Arial, sans-serif;
}

form {
  display: flex;
  margin-bottom: 20px;
}

input {
  flex-grow: 1;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px 0 0 4px;
  font-size: 16px;
}

button {
  padding: 10px 15px;
  background-color: #0066ff;
  color: white;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  font-size: 16px;
}

.todo-list {
  list-style-type: none;
  padding: 0;
}

.todo-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  margin-bottom: 10px;
  background-color: #f9f9f9;
  border-radius: 4px;
}

.completed span {
  text-decoration: line-through;
  color: #888;
}

.todo-list button {
  background-color: #ff3333;
  border-radius: 4px;
}

.empty-message {
  color: #888;
  text-align: center;
}

Next Steps in Your React Journey

After mastering the basics, you can expand your React knowledge with:

  1. React Router - For handling navigation in single-page applications
  2. State Management - Libraries like Redux or Zustand for complex state management
  3. Context API - React’s built-in solution for sharing state
  4. Custom Hooks - Building reusable logic
  5. React Testing - Using React Testing Library or Jest
  6. Performance Optimization - Memoization with useMemo and useCallback
  7. Server-Side Rendering - With frameworks like Next.js

Conclusion

React has revolutionized the way we build user interfaces by providing a component-based approach that is both powerful and flexible. By breaking down your UI into small, reusable components, you can create complex applications that are easier to maintain and update.

Remember that the best way to learn React is by building projects. Start with small applications and gradually take on more complex challenges as you become more comfortable with the library’s patterns and practices.

With its strong community support and extensive ecosystem, React continues to evolve and improve, making it an excellent choice for modern web development.