Components and Props

React.js is a popular JavaScript library for building user interfaces, primarily for single-page applications where you need a fast and interactive user experience. One of the core concepts in React is the use of components and props. In this chapter, we will explore these concepts in depth, starting from the basics and gradually moving towards more advanced topics.

Components in React

Components are the building blocks of any React application. They let you split the UI into independent, reusable pieces that can be managed separately. There are two main types of components in React:

  1. Functional Components
  2. Class Components

Functional Components

Functional components are JavaScript functions that return React elements. They are the simplest way to create a component in React.

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

				
			

Explanation:

  • Welcome is a functional component.
  • It takes props as an argument.
  • It returns a React element: an <h1> tag with a dynamic value {props.name}.
				
					// Usage
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

				
			
				
					// Output 
<h1>Hello, Sara</h1>

				
			

Class Components

Class components are more feature-rich than functional components. They extend from React.Component and have access to lifecycle methods.

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

				
			

Explanation:

  • Welcome is a class component.
  • It extends React.Component.
  • The render method returns a React element.
  • Access props using this.props.
				
					// Usage
<Welcome name="Sara" />

				
			
				
					// Output
<h1>Hello, Sara</h1>

				
			

Props in React

Props (short for properties) are read-only attributes used to pass data from a parent component to a child component.

Passing Props

Props are passed to components in the same way attributes are passed to HTML elements.

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

function App() {
  return <Greeting name="World" />;
}

				
			

Explanation:

  • Greeting component receives name as a prop.
  • App component passes name="World" to Greeting.
				
					// Output 
<h1>Hello, World</h1>

				
			

Default Props

You can define default values for props using the defaultProps property.

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

Greeting.defaultProps = {
  name: 'Stranger'
};

function App() {
  return <Greeting />;
}

				
			

Explanation:

  • Greeting.defaultProps sets name to 'Stranger' if no name prop is provided.
				
					// Output 
<h1>Hello, Stranger</h1>

				
			

Advanced Topics

Prop Types

Prop types are a mechanism to ensure that components receive props of the correct type. React has a built-in type-checking mechanism called prop-types.

				
					import PropTypes from 'prop-types';

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

Greeting.propTypes = {
  name: PropTypes.string.isRequired
};

function App() {
  return <Greeting name="World" />;
}

				
			

Explanation:

  • PropTypes.string.isRequired ensures name is a string and required.

Props Validation

You can add custom validation for props using functions.

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

Greeting.propTypes = {
  name: function(props, propName, componentName) {
    if (!/^[A-Z]/.test(props[propName])) {
      return new Error(
        `Invalid prop \`${propName}\` supplied to` +
        ` \`${componentName}\`. Validation failed.`
      );
    }
  }
};

function App() {
  return <Greeting name="world" />;
}

				
			

Explanation:

  • Custom validation ensures name starts with an uppercase letter.
				
					// Output
Error: Invalid prop `name` supplied to `Greeting`. Validation failed.

				
			

Composing Components

Components can be composed together to build more complex UIs.

				
					function Avatar(props) {
  return <img src={props.user.avatarUrl} alt={props.user.name} />;
}

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

				
			

Explanation:

  • Avatar, UserInfo, and Comment are composed together.
  • Each component is responsible for a part of the UI.
  • Props are passed down the component tree.

State vs Props

While props are used to pass data from parent to child, state is used to manage data within a component.

				
					class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = { date: new Date() };
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

				
			

Explanation:

  • Clock is a class component with state.
  • state holds the current date.
  • componentDidMount sets up a timer.
  • componentWillUnmount clears the timer.
  • tick updates the state.
  • render displays the current time.

Output:

A clock that updates every second.

Stateless vs Stateful Components

Stateless Components:

  • Do not manage state.
  • Rely solely on props.
				
					function Display(props) {
  return <h1>{props.message}</h1>;
}

				
			

Stateful Components:

  • Manage state.
  • Can change their output over time.
				
					class Display extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: 'Hello' };
  }

  updateMessage() {
    this.setState({ message: 'Goodbye' });
  }

  render() {
    return (
      <div>
        <h1>{this.state.message}</h1>
        <button onClick={() => this.updateMessage()}>Change</button>
      </div>
    );
  }
}

				
			

Explanation:

  • Display manages state and can update it.
  • updateMessage changes the state and re-renders the component.

Summary

Components: Independent, reusable pieces of UI.

  • Functional Components: Simple, stateless, written as functions.
  • Class Components: More powerful, can hold state, written as ES6 classes.

Props: Read-only attributes used to pass data from parent to child components.

  • Default Props: Default values for props if none are provided.
  • Prop Types: Type-checking for props.
  • State: Managed within the component, can change over time.
  • Composition: Building complex UIs by combining simpler components.
  • Stateless vs Stateful Components: Stateless rely on props, stateful manage their own state.

Example : User Profile Card

In this example, we will create a user profile card that displays user information passed through props.

Step 1: Create the UserProfile Component

UserProfile.js:

				
					import React from 'react';

function UserProfile(props) {
  return (
    <div className="user-profile">
      <img src={props.avatarUrl} alt={`${props.name}'s avatar`} />
      <h2>{props.name}</h2>
      <p>{props.bio}</p>
    </div>
  );
}

export default UserProfile;

				
			

Explanation:

  • The UserProfile component receives avatarUrl, name, and bio as props.
  • It renders a user profile card with an image, name, and bio.

Step 2: Use the UserProfile Component in an App

App.js:

				
					import React from 'react';
import UserProfile from './UserProfile';

function App() {
  const user = {
    avatarUrl: 'https://example.com/avatar.jpg',
    name: 'John Doe',
    bio: 'Software engineer with a passion for open-source projects.'
  };

  return (
    <div className="App">
      <UserProfile
        avatarUrl={user.avatarUrl}
        name={user.name}
        bio={user.bio}
      />
    </div>
  );
}

export default App;

				
			
				
					// Output 
<div class="App">
  <div class="user-profile">
    <img data-lazyloaded="1" src="data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs=" decoding="async" data-src="https://example.com/avatar.jpg" alt="John Doe's avatar" />
    <h2>John Doe</h2>
    <p>Software engineer with a passion for open-source projects.</p>
  </div>
</div>

				
			

Example : Todo List with Add and Remove Functionality

In this example, we will create a simple Todo list application where you can add and remove tasks.

Step 1: Create the TodoItem Component

TodoItem.js:

				
					import React from 'react';

function TodoItem(props) {
  return (
    <li>
      {props.task}
      <button onClick={() => props.removeTask(props.index)}>Remove</button>
    </li>
  );
}

export default TodoItem;

				
			

Explanation:

  • The TodoItem component receives task, index, and removeTask as props.
  • It renders a list item with the task and a remove button.

Step 2: Create the TodoList Component

TodoList.js:

				
					import React, { useState } from 'react';
import TodoItem from './TodoItem';

function TodoList() {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  const addTask = () => {
    if (newTask.trim()) {
      setTasks([...tasks, newTask]);
      setNewTask('');
    }
  };

  const removeTask = (index) => {
    setTasks(tasks.filter((task, i) => i !== index));
  };

  return (
    <div>
      <h1>Todo List</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="Add a new task"
      />
      <button onClick={addTask}>Add</button>
      <ul>
        {tasks.map((task, index) => (
          <TodoItem key={index} task={task} index={index} removeTask={removeTask} />
        ))}
      </ul>
    </div>
  );
}

export default TodoList;

				
			

Explanation:

  • The TodoList component uses state to manage tasks and the new task input.
  • The addTask function adds a new task to the list.
  • The removeTask function removes a task by index.
  • It renders an input field, an “Add” button, and a list of TodoItem components.

Usage: App.js

				
					import React from 'react';
import TodoList from './TodoList';

function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}

export default App;

				
			
				
					// Output //
<div class="App">
  <h1>Todo List</h1>
  <input type="text" placeholder="Add a new task" />
  <button>Add</button>
  <ul>
    <li>
      Sample Task
      <button>Remove</button>
    </li>
  </ul>
</div>

				
			

Example : Modal Component

In this example, we will create a modal component that can be opened and closed.

Step 1: Create the Modal Component

Modal.js:

				
					import React from 'react';
import './Modal.css'; // Assuming you have some basic CSS for the modal

function Modal(props) {
  if (!props.isOpen) {
    return null;
  }

  return (
    <div className="modal">
      <div className="modal-content">
        <span className="close" onClick={props.onClose}>&times;</span>
        <h2>{props.title}</h2>
        <div>{props.children}</div>
      </div>
    </div>
  );
}

export default Modal;

				
			

Explanation:

  • The Modal component receives isOpen, onClose, title, and children as props.
  • It renders a modal dialog when isOpen is true.

Step 2: Use the Modal Component in an App

App.js:

				
					import React, { useState } from 'react';
import Modal from './Modal';

function App() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

  return (
    <div className="App">
      <button onClick={openModal}>Open Modal</button>
      <Modal isOpen={isModalOpen} onClose={closeModal} title="My Modal">
        <p>This is the content of the modal.</p>
      </Modal>
    </div>
  );
}

export default App;

				
			
				
					// Output //
<div class="App">
  <button>Open Modal</button>
  <div class="modal">
    <div class="modal-content">
      <span class="close">&times;</span>
      <h2>My Modal</h2>
      <div>
        <p>This is the content of the modal.</p>
      </div>
    </div>
  </div>
</div>

				
			

Understanding components and props is fundamental to mastering React. Components are the building blocks of your UI, and props allow you to pass data between these components. By leveraging these concepts, you can build dynamic and reusable UI elements. As you progress, you'll encounter more advanced topics like state management and lifecycle methods, which further enhance your ability to create complex and efficient React applications.Happy coding !❤️

Table of Contents