Learnwizy Technologies Logo

Learnwizy Technologies

Class 16: Components, Props, and State

In React, the entire user interface is built using components. Components are independent, reusable pieces of UI. This class explores how to create components, pass data to them using props, and manage dynamic data within them using state.


React Components

At the heart of every React application are components. A component is a self-contained, reusable piece of UI. Think of them like Lego bricks – you can combine small, simple components to build larger, more complex ones.

Components allow us to:

Types of Components

Historically, React had two main ways to define components:

Modern React development primarily uses Function Components with Hooks due to their simplicity, readability, and better performance characteristics. We will focus on function components in this course.

Building Reusable Functional Components

In React, components are the building blocks of your UI. Functional components are JavaScript functions that return JSX. They are the most common way to write React components today, especially with the advent of Hooks.

Nesting Components

A core principle of React is breaking down the UI into smaller, manageable, and reusable components. This often involves nesting components within each other to create complex UI hierarchies, forming a parent-child relationship.

// src/components/Header.jsx
function Header() {
  return (
    <header>
      <nav>
        <a href="#">Home</a> | <a href="#">About</a> | <a href="#">Contact</a>
      </nav>
    </header>
  );
}

export default Header;

// src/components/Footer.jsx
function Footer() {
  return (
    <footer>
      <p>© {new Date().getFullYear()} My React App</p>
    </footer>
  );
}

export default Footer;

// src/App.jsx
import Header from './components/Header';
import Greeting from './components/Greeting';
import Footer from './components/Footer';

function App() {
  return (
    <div>
      <Header />
      <main>
        <h1>Welcome to My Application!</h1>
        <Greeting />
      </main>
      <Footer />
    </div>
  );
}

export default App;

Passing Data with Props

Props (short for "properties") are a way of passing data from a parent component to a child component. They are read-only, meaning a child component cannot directly modify the props it receives from its parent. Think of props as arguments to a function component.

Key characteristics of Props:

// src/components/UserCard.jsx
// UserCard component receives 'name' and 'age' as props
function UserCard(props) {
  return (
    <div className="user-card">
      <h3>Name: {props.name || 'Guest'}</h3> {/* No name prop, will use 'Guest' */}
      <p>Age: {props.age}</p>
    </div>
  );
}

export default UserCard;

// src/App.jsx (updated)
import UserCard from './components/UserCard';

function App() {
  return (
    <div>
      <h1>User Profiles</h1>
      {/* Pass data as attributes to the UserCard component */}
      <UserCard name="Alice" age={30} />
      <UserCard name="Bob" age={24} />
      <UserCard name="Charlie" age={45} />
      <UserCard />
    </div>
  );
}

export default App;

Accessing and Destructuring Props

You can access props using the props object, but it's common practice to use object destructuring in the function signature for cleaner code.

// src/components/UserCard.jsx (using destructuring)
// Destructure props directly in the function parameters
function UserCard({ name, age }) {
  return (
    <div className="user-card">
      <h3>Name: {name}</h3>
      <p>Age: {age}</p>
    </div>
  );
}

export default UserCard;

Managing Component State

While props allow data flow from parent to child, state is the data that is managed within a component and can change over time, triggering re-renders of the component.

The useState Hook

In function components, state is managed using the useState Hook, which is a function that takes one argument (the initial state value), and returns an array with two elements:

  1. The current state value.
  2. A function that lets you update the state value.
// src/components/Counter.jsx
import { useState } from 'react'; // Import useState

function Counter() {
  // Declare a state variable 'count' and a function 'setCount' to update it
  // Initial value of count is 0
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1); // Update the state
  };

  const decrement = () => {
    setCount(count - 1); // Update the state
  };

  return (
    <div className="counter">
      <h2>Count: {count}</h2>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

export default Counter;

// src/App.jsx (updated)
import Counter from './components/Counter';

function App() {
  return (
    <div>
      <h1>Interactive Counter</h1>
      <Counter />
    </div>
  );
}
export default App;

In this example, count starts at 0. When you click "Increment" or "Decrement", setCount is called, which updates the count state. React then sees that the state has changed and re-renders the Counter component to display the new count value.

Understanding State Updates

When you call the state setter function (e.g., setCount from useState), React schedules an update to the component's state. It's important to understand a few key aspects of how these updates work:


List Rendering

Displaying collections of data, such as lists of products, users, or comments, is a very common task in web development. In React, you typically render lists using the JavaScript map() array method, which transforms an array of data into an array of React elements.

Key concepts for list rendering:

The key Prop Explained

Rendering a List of Items

Let's display a list of fruits:

// src/components/FruitList.jsx
function FruitList() {
  const fruits = [
    { id: 1, name: 'Apple' },
    { id: 2, name: 'Banana' },
    { id: 3, name: 'Cherry' },
  ];

  return (
    <div>
      <h2>My Fruit List</h2>
      <ul>
        {fruits.map((fruit) => (
          <li key={fruit.id}>{fruit.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default FruitList;

In this example, we map over the fruits array. For each fruit object, we return an <li> element. The key={fruit.id} is crucial here, as fruit.id provides a stable and unique identifier for each list item.


Basic Component Lifecycle (Conceptual)

While React handles most of the complexities of rendering, understanding the basic "lifecycle" of a component can be helpful. A component goes through three main phases:

The useEffect Hook, with its dependency array, allows you to "hook into" these lifecycle phases in functional components. For example, an empty dependency array ([]) makes useEffect behave similarly to componentDidMount (runs once after mounting).

Crucially, when a component's state (or props) changes, React automatically triggers an update, meaning the component's render function (and useEffect if dependencies change) will run again to reflect the new data. This is how React keeps your UI in sync with your application's data.