DEV Community

itric
itric

Posted on • Edited on

STRUGGLING with React? LEARN the Step-by-Step UI BUILDING Guide!

To utilize and harness the full power of React, it’s essential to adopt a specific mindset known as "Thinking in React". This approach involves breaking down the UI into components, managing state effectively, and ensuring a seamless data flow. In this article, we’ll explore the principles of Thinking in React and provide a step-by-step guide to building efficient and maintainable user interfaces with a practical example. And along the way we will map it to analogy for better understanding.

(code used in this article :https://github.com/code-blogger3/Youtube-demos/blob/main/src/App.jsx )

Understanding the Core Principles

Before diving into the practical aspects of Thinking in React, it’s important to grasp the core principles that underpin this methodology:

  1. Component-Based Architecture: React promotes the idea of building user interfaces using small, reusable components. Each component encapsulates a piece of the UI, making it easier to manage and maintain.
  2. Unidirectional Data Flow: Data in React applications flows in one direction, from parent components to child components. This predictable data flow simplifies debugging and enhances the application’s stability.
  3. State Management: React components can maintain their own state, which represents the dynamic parts of the UI. Understanding how to manage state effectively is crucial for building interactive applications.

Step-by-Step Guide to Thinking in React

To illustrate the process of Thinking in React, let’s walk through the steps of building a simple searchable product data table.

1. Break the UI into a Component Hierarchy

The first step is to break down the UI into a component hierarchy. This involves identifying the distinct parts of the interface and mapping them to components. For our example, the product data table can be divided into the following components:

  • FilterableProductTable: The main container component.
  • SearchBar: A component for the search input.
  • ProductTable: A component that displays the product data.
  • ProductCategoryRow: A component for displaying product category headers.
  • ProductRow: A component for displaying individual products.

Now let’s relate this step to an analogy.

Imagine you're a child again, surrounded by a sea of colorful Lego bricks. You have a vision in your mind—a towering castle with intricate details, bustling marketplaces, and brave knights. But how do you transform this vision into reality? Well, The answer lies in a step-by-step process, much like Thinking in React. Let’s see how building a complex Lego structure mirrors the process of developing a user interface with React.

The Lego Metaphor: Building Block by Block

Breaking the UI into a component Hierarchy is similar to breaking Down the blueprint of the Castle.

Breaking Down the Castle (Component Hierarchy)

When you start building your Lego castle, you don't just randomly stick pieces together. Instead, you think about the different parts of the castle: the towers, the walls, the gates, and the moat. In React, this is akin to breaking down your user interface into a component hierarchy.

  • The Castle: This is your entire application, the big picture.
  • The Towers and Walls: These represent the major sections of your app, like the header, main content, and footer.
  • The Gates and Moat: These are smaller parts within those sections, like buttons, forms, and navigation links.

By breaking the castle into manageable parts, you can focus on building one section at a time, ensuring each piece fits perfectly with the others.

2. Build a Static Version in React

Once the component hierarchy is established, the next step is to build a static version of the UI in React. This involves creating the components and rendering them with static data. At this stage, focus on the layout and structure without adding any interactivity or state management.

This step corresponds to creating static structures of the castle.

Creating Static Structures (Static Version in React)

Before adding the finishing touches and intricate details to your Lego castle, you first build a static version. You assemble the towers, walls, gates, and moat without any moving parts or decorations. In React, this is similar to creating a static version of your UI with components that don't yet have interactivity or dynamic behavior.

  • Lego Bricks: These are your React components, each serving a specific purpose.
  • Building the Structure: You put together the components to form the basic layout of your app.

This step helps you visualize the overall structure and ensures that all parts fit together seamlessly.

3. Identify the Minimal (But Complete) Representation of UI State

To add interactivity, it’s essential to identify the minimal set of mutable state that the application needs. For our product data table, the state can be categorized into two main types:

  • Search Query: The text entered in the search bar.
  • Stock Only: A boolean value indicating whether to show only products in stock.

Now this step maps to deciding which bricks can move in castle.

Deciding Which Bricks Can Move (Identifying State)

Now that your static Lego castle is standing tall, you want to add some excitement—maybe a drawbridge that opens, knights that move, and flags that wave in the wind. To do this, you need to identify which parts of the castle should be dynamic. In React, this involves determining the minimal but complete set of state needed for your app.

  • Dynamic Lego Parts: These are the movable pieces, like the drawbridge and knights.
  • UI State: This is the state in React, such as the current page, user input, or whether a modal is open.

By identifying these dynamic parts, you can focus on making only the necessary pieces interactive, keeping your castle (and your code) efficient and manageable.

4. Identify Where Your State Should Live

Next, determine which components should own the state. According to the React documentation, state should be owned by the component that:

  • Renders something based on that state.
  • Needs to pass the state down to its child components.

For our example, the FilterableProductTable component is the best candidate to hold the state, as it renders both the SearchBar and the ProductTable and needs to pass the state to these child components.

This step overlaps to deciding who will control the drawbridge in the castle, where the state lives.

Deciding Who Controls the Drawbridge (Where State Lives)

In your Lego castle, someone needs to control the drawbridge. Should it be the knight, the king, or perhaps a hidden mechanism? In React, this is like deciding which component should own the state.

  • Drawbridge Mechanism: This represents the state in React.
  • Controller: This is the component that manages the state, determining how and when it changes.

By placing the state in the appropriate component, you ensure a smooth and logical flow of information, making it easier to manage and update your app.

5. Add Inverse Data Flow

Finally, add inverse data flow to make the components interactive. This involves passing callback functions from the stateful parent component to the stateless child components. The child components call these functions to update the state in the parent component. For instance, the SearchBar component will receive a callback from FilterableProductTable to update the search query and stock-only state.

This step can be looked as allowing communication between knights.

Communicating Between Knights (Adding Inverse Data Flow)

To make your Lego castle come alive, the knights need to communicate with each other—when the drawbridge is down, they charge out; when it's up, they retreat. In React, this involves adding inverse data flow, where child components send data back to their parent components.

  • Knight Communication: This is like child components sending information back to their parents.
  • Parent Commands: The parent component uses this information to update the state and trigger actions.

This two-way communication ensures that all parts of your castle (and your app) work together harmoniously, creating a dynamic and interactive experience.

Practical Example: Building the Searchable Product Data Table

Let’s put these principles into practice by building the searchable product data table. Below is a simplified implementation in React:

import React, { useState } from 'react';

// Sample product data
const PRODUCTS = [
  { category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football" },
  { category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball" },
  { category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball" },
  { category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch" },
  { category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5" },
  { category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7" }
];

// FilterableProductTable Component
function FilterableProductTable() {
  const [filterText, setFilterText] = useState('');
  const [inStockOnly, setInStockOnly] = useState(false);

  return (
    <div>
      <SearchBar
        filterText={filterText}
        inStockOnly={inStockOnly}
        onFilterTextChange={setFilterText}
        onInStockChange={setInStockOnly}
      />
      <ProductTable
        products={PRODUCTS}
        filterText={filterText}
        inStockOnly={inStockOnly}
      />
    </div>
  );
}

// SearchBar Component
function SearchBar({ filterText, inStockOnly, onFilterTextChange, onInStockChange }) {
  return (
    <form>
      <input
        type="text"
        placeholder="Search..."
        value={filterText}
        onChange={(e) => onFilterTextChange(e.target.value)}
      />
      <p>
        <input
          type="checkbox"
          checked={inStockOnly}
          onChange={(e) => onInStockChange(e.target.checked)}
        />
        {' '}
        Only show products in stock
      </p>
    </form>
  );
}

// ProductTable Component
function ProductTable({ products, filterText, inStockOnly }) {
  const rows = [];
  let lastCategory = null;

  products.forEach((product) => {
    if (product.name.indexOf(filterText) === -1 || (!product.stocked && inStockOnly)) {
      return;
    }
    if (product.category !== lastCategory) {
      rows.push(
        <ProductCategoryRow
          category={product.category}
          key={product.category}
        />
      );
    }
    rows.push(
      <ProductRow
        product={product}
        key={product.name}
      />
    );
    lastCategory = product.category;
  });

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
}

// ProductCategoryRow Component
function ProductCategoryRow({ category }) {
  return (
    <tr>
      <th colSpan="2">
        {category}
      </th>
    </tr>
  );
}

// ProductRow Component
function ProductRow({ product }) {
  const name = product.stocked ? product.name : <span style={{ color: 'red' }}>{product.name}</span>;

  return (
    <tr>
      <td>{name}</td>
      <td>{product.price}</td>
    </tr>
  );
}

// Render the FilterableProductTable component
function App() {
  return (
    <div>
      <h1>Product Table</h1>
      <FilterableProductTable />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Additional resource: https://elementarydot.blogspot.com/2024/07/thinking-in-react-relatable-scenarios.html

Top comments (0)