React Lecture 3: Forms, Conditional Content, and State Management - MantsSk/CA_PTUA14 GitHub Wiki

Lecture 3: Forms, Conditional Content, and State Management

In this lecture, you will build on your knowledge from Lectures 1 and 2 by adding dynamic functionality to your news website. Specifically, you will learn how to:

  • Design Reusable Components: Create a general-purpose modal that can wrap any content.
  • Build Controlled Forms: Use state to capture and manage user input.
  • Implement Conditional Rendering: Show or hide content based on user interactions.
  • Practice State Management: Update complex component state, such as dynamically adding new articles.

1. Creating a General-Purpose Modal Component

A well-designed modal should be reusable across your project. We achieve this by creating a component that:

  • Accepts a show prop to determine visibility.
  • Uses the children prop to render arbitrary content.
  • Provides an onClose callback to signal when it should be hidden.

src/components/Modal.jsx

import React from 'react';
import './Modal.css';

function Modal({ show, onClose, children }) {
  if (!show) return null;

  return (
    <div className="modal-overlay">
      <div className="modal">
        <button className="close-button" onClick={onClose}>X</button>
        {children}
      </div>
    </div>
  );
}

export default Modal;

src/components/Modal.css

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal {
  background: #fff;
  padding: 30px;
  border-radius: 8px;
  width: 90%;
  max-width: 500px;
}
.close-button {
  position: absolute;
  top: 10px;
  right: 10px;
  cursor: pointer;
}

Detailed Explanation:

  • Conditional Rendering: The if (!show) return null; statement prevents the modal from rendering when it’s not needed, keeping the DOM clean.
  • Reusability via children: By wrapping the modal’s content in {children}, this component can be used to display any type of content—not just forms.
  • Event Propagation: The close button triggers onClose so that the parent component can manage the modal’s visibility state.

2. Building the New Article Form Component

The New Article form is a controlled form component that gathers user input for new articles. It demonstrates key concepts like input handling, form submission, and state reset.

src/components/NewArticleForm.jsx

import React, { useState } from 'react';

function NewArticleForm({ onSubmit }) {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit({ title, content });
    setTitle("");
    setContent("");
  };

  return (
    <form onSubmit={handleSubmit}>
      <h2>Create New Article</h2>
      <div className="form-group">
        <label htmlFor="title">Title:</label>
        <input 
          id="title"
          type="text" 
          value={title} 
          onChange={(e) => setTitle(e.target.value)}
          required 
        />
      </div>
      <div className="form-group">
        <label htmlFor="content">Content:</label>
        <textarea 
          id="content"
          value={content} 
          onChange={(e) => setContent(e.target.value)}
          required 
        />
      </div>
      <button type="submit">Add Article</button>
    </form>
  );
}

export default NewArticleForm;

3. Integrating Modal and Form with the Article List

src/components/ArticleList.jsx

import React, { useState } from 'react';
import Article from './Article';
import Modal from './Modal';
import NewArticleForm from './NewArticleForm';
import './ArticleList.css';

function ArticleList() {
  const [articles, setArticles] = useState([
    { title: "React 101: Getting Started", content: "Learn the basics of React, a powerful UI library." },
    { title: "Understanding JSX", content: "JSX is a syntax extension for JavaScript that allows writing HTML in React." },
    { title: "Component Reusability in React", content: "Building modular components improves scalability and maintainability." }
  ]);

  const [showModal, setShowModal] = useState(false);

  const handleAddArticle = (newArticle) => {
    setArticles(prevArticles => [...prevArticles, newArticle]);
    setShowModal(false);
  };

  return (
    <div className="article-list-container">
      <button onClick={() => setShowModal(true)}>Add New Article</button>
      <Modal show={showModal} onClose={() => setShowModal(false)}>
        <NewArticleForm onSubmit={handleAddArticle} />
      </Modal>
      <div className="article-list">
        {articles.map((article, index) => (
          <Article key={index} title={article.title} content={article.content} />
        ))}
      </div>
    </div>
  );
}

export default ArticleList;

src/components/NewArticleForm.css

This CSS could be spread out among components you already created.

/* ArticleFormList.css */

/* Container for the article list and form */
.article-list-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

/* Button to add a new article */
.article-list-container button {
  background-color: #3498db;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
  margin-bottom: 20px;
  font-size: 16px;
}

.article-list-container button:hover {
  background-color: #2980b9;
}

/* Styling for the list of articles */
.article-list {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Individual article item */
.article {
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 15px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* Form styling */
.form-group {
  margin-bottom: 15px;
}

.form-group label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.form-group input,
.form-group textarea {
  width: 100%;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.form-group textarea {
  resize: vertical;
}

/* Submit button styling */
button[type="submit"] {
  background-color: #27ae60;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
}

button[type="submit"]:hover {
  background-color: #219150;
}

4. Recap and Key Takeaways

What You've Learned:

Component Reusability: You built a general-purpose Modal component that can be used to wrap any content, emphasizing component reusability and separation of concerns.

Controlled Forms: By managing form input through state, you learned how to capture, validate, and submit user data in a predictable manner.

Conditional Rendering: You practiced showing and hiding UI elements based on state, which is critical for creating dynamic, responsive applications.

State Management with useState: You enhanced your application’s interactivity by managing multiple state values—from toggling modals to updating lists—demonstrating the power of React’s state management.

Practical Exercise

1st exercise

Improve your application according to what we did in lecture

2nd exercise

Introduce a smooth fade-in/fade-out effect when the modal opens or closes.

Modify the existing Modal.jsx and Modal.css files.

Add CSS transitions to animate the modal’s appearance and disappearance.

Ensure that the component still respects the show prop for conditional rendering.

3rd exercise

Use conditional rendering to enhance user feedback.

In ArticleList.jsx, check the length of the articles array.

If no articles exist, display a message such as “No articles available. Please add a new article.”

Otherwise, display the list of articles as usual.

4th exercise

Allow users to edit articles already added to the list.

⚠️ **GitHub.com Fallback** ⚠️