Share

Core React Concepts: Building Dynamic User Interfaces

by ObserverPoint · June 29, 2025

You’ve installed Node.js and set up your first React project. Now, let’s dive into the fundamental concepts that make React such a powerful and popular library for building dynamic user interfaces. Understanding these core ideas — components, JSX, props, state, and rendering techniques — is crucial for writing efficient, maintainable, and interactive React applications. This knowledge forms the bedrock for more advanced topics like Redux for state management or working with TypeScript for type safety.

React is all about breaking down your UI into reusable, self-contained pieces called components. Once you grasp how these components interact, how data flows through them, and how they react to changes, you’ll be well on your way to mastering front-end development with React. Let’s unlock the power of declarative UI programming! —

What is a Component? (Functional vs. Class Components)

At its heart, a React component is an independent, reusable piece of code that defines a part of your UI. Think of them as custom HTML elements. They receive inputs (called props) and return React elements that describe what should appear on the screen. There are two primary ways to define components in React:

Functional Components

These are simple JavaScript functions that accept `props` as an argument and return JSX. With the introduction of React Hooks (which we’ll touch upon shortly), functional components gained the ability to manage state and side effects, making them the preferred choice for new development due to their simplicity, readability, and testability. They are often less verbose than class components.

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

// Usage:
// <WelcomeMessage name="Alice" />
    

Class Components

These are ES6 classes that extend `React.Component` and require a `render()` method that returns JSX. Historically, class components were the only way to manage state and use lifecycle methods. While still fully supported and found in older codebases, they are generally less common for new components now that Hooks provide equivalent functionality for functional components. They involve using `this` keyword, which can sometimes be a source of confusion.

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

// Usage:
// <WelcomeMessageClass name="Bob" />
    

JSX Syntax: JavaScript XML

JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like markup directly within your JavaScript code. It’s not a new language, but a syntactic sugar for `React.createElement()` calls. Browsers don’t understand JSX directly; it gets transpiled into regular JavaScript by a tool like Babel before the browser executes it.

JSX makes writing UI components in React intuitive and declarative, as it closely resembles the HTML structure you’re building. Key things to remember about JSX:

  • Expressions in curly braces: You can embed any valid JavaScript expression inside JSX by wrapping it in curly braces `{}`.
  • CamelCase for attributes: HTML attributes like `class` become `className` in JSX because `class` is a reserved keyword in JavaScript. Similarly, `for` becomes `htmlFor`.
  • Self-closing tags: HTML elements that don’t have children must be explicitly self-closed (e.g., `<img />`, `<input />`).
  • Single root element: A JSX expression must have only one outermost element. If you need to return multiple elements, wrap them in a parent `<div>` or use a `<Fragment>` (`<>…</>`) to avoid adding an extra node to the DOM.
function MyComponent() {
    const userName = "Charlie";
    const isActive = true;

    return (
        <div className="app-container">
            <h2>Hello, {userName}!</h2>
            <p>Status: {isActive ? 'Online' : 'Offline'}</p>
            <img src="logo.png" alt="Company Logo" />
        </div>
    );
}
    

Props: Passing Data Down

Props (short for “properties”) are how you pass data from a parent component to a child component in React. They are read-only, meaning a child component should never directly modify the props it receives. This “unidirectional data flow” makes React applications easier to understand and debug.

Props are passed to components as attributes in JSX, similar to how you’d pass attributes to HTML elements. Inside the child component, `props` are received as an object. For functional components, they are the first argument of the function; for class components, they are accessible via `this.props`.

// Parent Component
function App() {
    return (
        <div>
            <UserProfile name="Dave" age={30} />
            <UserProfile name="Eve" age={25} />
        </div>
    );
}

// Child Component (UserProfile.js)
function UserProfile(props) {
    return (
        <div>
            <h3>Name: {props.name}</h3>
            <p>Age: {props.age}</p>
        </div>
    );
}
    

You can also destructure props for cleaner code:

function UserProfile({ name, age }) { // Destructuring props directly
    return (
        <div>
            <h3>Name: {name}</h3>
            <p>Age: {age}</p>
        </div>
    );
}
    

State: Managing Component-Specific Data with `useState`

While `props` are for data passed from parent to child, state is for data that a component manages internally and that can change over time, typically due to user interaction or network requests. When a component’s state changes, React automatically re-renders that component and its children to reflect the new data.

For functional components, React Hooks provide the `useState` Hook to manage state. `useState` returns an array with two elements: the current state value and a function to update that value. It’s conventional to use array destructuring to name these. The argument to `useState` is the initial state value.

import React, { useState } from 'react';

function Counter() {
    // Declares a state variable 'count' and a setter function 'setCount'
    // Initial state is 0
    const [count, setCount] = useState(0);

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

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

When the `setCount` function is called, React re-renders the `Counter` component, and the `count` value in the JSX will reflect the updated state. —

Component Lifecycle (Briefly for Class Components; Focus on Hooks for Functional)

Every React component goes through a “lifecycle” of phases: mounting (when it’s created and inserted into the DOM), updating (when it re-renders due to prop or state changes), and unmounting (when it’s removed from the DOM).

Class Component Lifecycle Methods (Briefly)

Class components use special methods to “hook into” these phases:

  • `componentDidMount()`: Called after the component is rendered to the DOM. Good for data fetching or setting up subscriptions.
  • `componentDidUpdate(prevProps, prevState)`: Called after a component updates. Useful for reacting to prop or state changes.
  • `componentWillUnmount()`: Called just before a component is removed from the DOM. Ideal for cleanup (e.g., clearing timers, cancelling network requests).

Functional Components and the `useEffect` Hook

For functional components, the `useEffect` Hook is your primary tool for handling side effects and mimicking lifecycle behavior. It runs after every render by default. You can control when it runs by providing a dependency array as its second argument:

  • `useEffect(() => { /* effect */ })`: Runs after every render.
  • `useEffect(() => { /* effect */ }, [])`: Runs only once after the initial render (like `componentDidMount`). The empty array means it has no dependencies, so it won’t re-run unless the component itself is re-mounted.
  • `useEffect(() => { /* effect */ }, [prop1, state2])`: Runs after the initial render and whenever `prop1` or `state2` changes.
  • `useEffect(() => { /* effect */ return () => { /* cleanup */ }; }, […])`: The return function is a cleanup function, running before the component unmounts or before the effect re-runs. This is similar to `componentWillUnmount`.
import React, { useState, useEffect } from 'react';

function DataFetcher() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        // This effect runs once after the initial render (like componentDidMount)
        // because of the empty dependency array [].
        fetch('https://api.example.com/data')
            .then(response => response.json())
            .then(json => {
                setData(json);
                setLoading(false);
            })
            .catch(error => {
                console.error("Error fetching data:", error);
                setLoading(false);
            });

        // Cleanup function (like componentWillUnmount)
        return () => {
            // Cancel any pending network requests or clean up subscriptions here
            console.log("Component unmounted or effect re-ran.");
        };
    }, []); // Empty dependency array means run once on mount and cleanup on unmount

    if (loading) {
        return <p>Loading data...</p>;
    }

    return <div>Data: {JSON.stringify(data)}</div>;
}
    

Conditional Rendering and List Rendering

React provides powerful ways to dynamically render content based on conditions or to display collections of data.

Conditional Rendering

This means rendering different elements or components based on a specific condition. Common methods include:

  • `if`/`else` statements: Inside a component’s function (or `render` method for class components), you can use standard JavaScript `if`/`else` to return different JSX.
  • Ternary operator: A concise way to handle two conditions, often directly within JSX: `condition ? <ComponentA /> : <ComponentB />`.
  • Logical `&&` operator: If you want to render something only if a condition is true, you can use `condition && <Component />`. If `condition` is false, nothing is rendered.
function Greeting({ isLoggedIn }) {
    return (
        <div>
            {isLoggedIn ? (
                <h2>Welcome back!</h2>
            ) : (
                <p>Please log in.</p>
            )}
        </div>
    );
}

function AdminPanel({ isAdmin }) {
    return (
        <div>
            {isAdmin && <h3>Admin Controls</h3>}
        </div>
    );
}
    

List Rendering

When you have a collection of data (e.g., an array) and want to display a list of items, you typically use the JavaScript `map()` array method to transform each item into a React element. Each item in a list *must* have a unique `key` prop. React uses keys to efficiently update and re-render only the items that have changed, been added, or removed. Without unique keys, React might behave unexpectedly or issue warnings.

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

// Example usage:
// const myTodos = [
//     { id: 1, text: 'Learn React' },
//     { id: 2, text: 'Build a project' },
//     { id: 3, text: 'Deploy to Netlify' },
// ];
// <TodoList todos={myTodos} />
    

These core concepts — components as building blocks, JSX for declarative UI, props for data flow, state for dynamic data, lifecycle management with `useEffect`, and efficient rendering techniques — are the pillars of React development. Mastering them will empower you to create complex and engaging user interfaces, paving the way for your journey into advanced React patterns and ecosystem tools. —

References

You may also like