skip to Main Content

I’m creating bookshop application. I’m quite freshmen in programing world. Whole application is created in react.

My mission is to create modal component from existing "update" component.

The main component is books where is an update button, I would like the "Update" component to appear as a modal component after pressing "update – button" I have tried various ways to do this but I am stuck on this issue for good (2 days),

import React from "react";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import "../style/style.css"

const Books = () => {
  const [books, setBooks] = useState([]);

  useEffect(() => {
    const fetchAllBooks = async () => {
      try {
        const res = await axios.get("http://localhost:8800/books");
        setBooks(res.data);
      } catch (error) {
        console.log(error);
      }
    };
    fetchAllBooks();
  }, []);

  const handleDelete = async (id) => {
    try {
      await axios.delete(`http://localhost:8800/books/${id}`);
      window.location.reload();
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div className="form">
      <h1>The Greatest Bookshop</h1>
      <div className="books">
        {books.map((book) => (
          <div
            key={book.id}
            className="book"
          >
            {book.cover && (
              <img
                className="mainImg"
                src={book.cover}
                alt=""
              />
            )}
            <h2>{book.title}</h2>
            <p>{book.desc}</p>
            <span>${book.price}</span>
            <button
              className="delete"
              onClick={() => handleDelete(book.id)}
            >
              Delete
            </button>
            <button className="update">
              <Link
                to={`/update/${book.id}`}
                style={{ color: "inherit", textDecoration: "none" }}
              >
                Update
              </Link>
            </button>
          </div>
        ))}
      </div>
      <button className="newBook">
        <Link
          to="/Add"
          style={{ color: "inherit", textDecoration: "none" }}
        >
          Add new book
        </Link>
      </button>
    </div>
  );
};

export default Books;

And component which need to be modal is "Update":

import React, { useState, useEffect, useRef } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import axios from "axios";
import "../style/update.css";
// import EditCover from "../__Test__/EditCover";

const Update = () => {

  const navigate = useNavigate();
  const location = useLocation();

  const bookId = location.pathname.split("/")[2];

  const [book, setBook] = useState({
    title: "",
    desc: "",
    price: null,
    cover: "",
  });

  useEffect(() => {
    const fetchCoverUrl = async () => {
      try {
        const res = await axios.get(`http://localhost:8800/books/${bookId}`);
        setBook((prevBook) => ({ ...prevBook, cover: res.data[0].cover}));
      } catch (error) {
        console.log(error);
      }
    };
    fetchCoverUrl();
  }, [bookId]);

  const fileInputRef = useRef(null);
 
  const changeImage = () => {
    fileInputRef.current.click();
  };

  const handleChange = (e) => {
    setBook((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setBook((prev) => ({...prev, cover: URL.createObjectURL(file),
  }));
  };

  const handleClick = async (e) => {
    e.preventDefault();
    try {
      await axios.put("http://localhost:8800/books/" + bookId, book);
      navigate("/");
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div className="update-form">
      <div className="update-container">
        <h1>Update the Book</h1>
        <input
          type="text"
          placeholder="title"
          onChange={handleChange}
          name="title"
        />
        <input
          type="text"
          placeholder="desc"
          onChange={handleChange}
          name="desc"
        />
        <input
          type="number"
          placeholder="price"
          onChange={handleChange}
          name="price"
        />
        <input
          data-id="inputRef"
          type="file"
          placeholder="cover"
          ref={fileInputRef}
          onChange={handleFileChange}
          name="cover"
          style={{display: "none"}}
        />
        <button
          className="updateBtn"
          onClick={handleClick}
        >
          Update
        </button>
      </div>
      <div className="coverForm">
        {book.cover && (
              <img
                className="coverUpdate"
                src={book.cover}
                alt="Loading..."
                onClick={changeImage}
                style={{cursor: "pointer"}}
              />
        )}
      </div>
    </div>
  );
};

export default Update;

I am not really sure how i can figure existing "update" component to be a modal, i was basing on example:

import React, { useState } from 'react'
import "./test.css"


const ModalComponent = () => {

    const [isModal, setIsModal] = useState(false);
    
    const openModal = () => {
        setIsModal(true);
        
    }

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


  return (
    <div>
      <h1>Books</h1>
      <button onClick={openModal}>Update</button>

      {isModal && (
        <div className="modal">
          <div className="modal-content">
            <h2>Update book</h2>
            <button className="close-btn" onClick={closeModal}>Close</button>
          </div>
        </div>
      )}
    </div>
  )
}

export default ModalComponent

2

Answers


  1. You need to wrap update component in modal-content and open modal on click of update button.

    Creating a Modal using HTML CSS JS

    You dont need to create a separate component for Modal if you only want to use this modal one time.

    Login or Signup to reply.
  2. If you want the Update component content to open in the ModalComponent then Update should import ModalComponent and render the UI into it. ModalComponent should be refactored a bit to take and render a children prop.

    Example:

    Assume the modal is open by default when rendered/mounted, closed when unmounted.

    const ModalComponent = ({ children, onClose }) => {
      return (
        <div className="modal">
          <div className="modal-content">
            <button className="close-btn" onClick={onClose}>Close</button>
            {children}
          </div>
        </div>
      );
    };
    

    From here you have a couple choices. You can import ModalComponent into Update and wrap directly the ui there:

    import React, { useState, useEffect, useRef } from "react";
    import { useNavigate, useLocation, useParams } from "react-router-dom";
    import axios from "axios";
    import "../style/update.css";
    // import EditCover from "../__Test__/EditCover";
    import { ModalComponent } from "../path/to/ModalComponent";
    
    const Update = () => {
      const navigate = useNavigate();
      const location = useLocation();
      const { bookId } = useParams();
    
      const [book, setBook] = useState({
        title: "",
        desc: "",
        price: null,
        cover: "",
      });
    
      useEffect(() => {
        const fetchCoverUrl = async () => {
          try {
            const res = await axios.get(`http://localhost:8800/books/${bookId}`);
            setBook((prevBook) => ({ ...prevBook, cover: res.data[0].cover}));
          } catch (error) {
            console.log(error);
          }
        };
        fetchCoverUrl();
      }, [bookId]);
    
      ...
    
      return (
        <ModalComponent onClose={() => navigate(-1)}> // <-- navigate back to list
          <div className="update-form">
            <div className="update-container">
              ...
            </div>
            ...
          </div>
        </ModalComponent>
      );
    };
    
    export default Update;
    

    or you can create a layout route component that renders the Update route into the modal.

    import { Outlet, useNavigate } from "react-router-dom";
    import { ModalComponent } from "../path/to/ModalComponent";
    
    const UpdateLayout = () => {
      const navigate = useNavigate();
    
      return (
        <ModalComponent onClose={() => navigate(-1)}>
          <Outlet />
        </ModalComponent>
      );
    };
    
    <Routes>
      ...
      <Route element={<UpdateLayout />}>
        <Route path="/update/:bookId" element={<Update />} />
      </Route>
      ...
    </Routes>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search