Membina Laman Web Pencari Resipi menggunakan React

DDD
Lepaskan: 2024-09-13 12:33:30
asal
1193 orang telah melayarinya

Building Recipe Finder Website using React

pengenalan

Dalam blog ini, kami akan membina Tapak Web Pencari Resipi menggunakan React. Apl ini membolehkan pengguna mencari resipi kegemaran mereka, melihat arah aliran atau resipi baharu dan menyimpan resipi kegemaran mereka. Kami akan memanfaatkan Edamam API untuk mengambil data resipi masa nyata dan memaparkannya secara dinamik di tapak web.

Gambaran Keseluruhan Projek

Pencari Resipi membolehkan pengguna untuk:

  • Cari resipi mengikut nama.
  • Lihat sohor kini dan resipi yang baru ditambah.
  • Lihat maklumat terperinci tentang resipi individu.
  • Tambah resipi pada senarai kegemaran dan kekalkan data menggunakan localStorage.

Ciri-ciri

  • Fungsi Carian: Pengguna boleh mencari resipi dengan memasukkan pertanyaan.
  • Resipi Arah Aliran: Memaparkan resipi yang menjadi sohor kini daripada API.
  • Resipi Baharu: Memaparkan resipi terbaharu daripada API.
  • Perincian Resipi: Memaparkan maklumat terperinci tentang resipi yang dipilih.
  • Kegemaran: Membenarkan pengguna menambah resipi pada senarai kegemaran, yang disimpan secara setempat.

Teknologi yang Digunakan

  • React: Untuk membina antara muka pengguna.
  • Penghala Reaksi: Untuk navigasi antara halaman yang berbeza.
  • Edamam API: Untuk mengambil resipi.
  • CSS: Untuk menggayakan aplikasi.

Struktur Projek

src/
│
├── components/
│   └── Navbar.js
│
├── pages/
│   ├── Home.js
│   ├── About.js
│   ├── Trending.js
│   ├── NewRecipe.js
│   ├── RecipeDetail.js
│   ├── Contact.js
│   └── Favorites.js
│
├── App.js
├── index.js
├── App.css
└── index.css
Salin selepas log masuk

Pemasangan

Untuk menjalankan projek ini secara tempatan, ikut langkah berikut:

  1. Klon repositori:
   git clone https://github.com/abhishekgurjar-in/recipe-finder.git
   cd recipe-finder
Salin selepas log masuk
  1. Pasang kebergantungan:
   npm install
Salin selepas log masuk
  1. Mulakan apl React:
   npm start
Salin selepas log masuk
  1. Dapatkan bukti kelayakan Edamam API anda (ID API dan Kunci API) daripada tapak web Edamam.

  2. Tambahkan bukti kelayakan API anda di dalam halaman tempat panggilan API dibuat, seperti Home.js, Trending.js, NewRecipe.js dan RecipeDetail.js.

Penggunaan

App.js
import React from "react";
import Navbar from "./components/Navbar";
import { Route, Routes } from "react-router-dom";
import "./App.css";
import Home from "./pages/Home";
import About from "./pages/About";
import Trending from "./pages/Trending";
import NewRecipe from "./pages/NewRecipe";
import RecipeDetail from "./pages/RecipeDetail";
import Contact from "./pages/Contact";
import Favorites from "./pages/Favorites";
const App = () => {
  return (
    <>
      <Navbar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/trending" element={<Trending />} />
        <Route path="/new-recipe" element={<NewRecipe />} />
        <Route path="/new-recipe" element={<NewRecipe />} />
        <Route path="/recipe/:id" element={<RecipeDetail />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact/>} />
        <Route path="/favorites" element={<Favorites/>} />
      </Routes>
   <div className="footer">
   <p>Made with ❤️ by Abhishek Gurjar</p>
   </div>
    </>
  );
};

export default App;

Salin selepas log masuk
Home.js

Ini ialah halaman utama di mana pengguna boleh mencari resipi menggunakan API Edamam.

import React, { useState, useRef, useEffect } from "react";
import { IoSearch } from "react-icons/io5";
import { Link } from "react-router-dom";


const Home = () => {
  const [query, setQuery] = useState("");
  const [recipe, setRecipe] = useState([]);
  const recipeSectionRef = useRef(null);

  const API_ID = "2cbb7807";
  const API_KEY = "17222f5be3577d4980d6ee3bb57e9f00";

  const getRecipe = async () => {
    if (!query) return; // Add a check to ensure the query is not empty
    const response = await fetch(
      `https://api.edamam.com/search?q=${query}&app_id=${API_ID}&app_key=${API_KEY}`
    );
    const data = await response.json();
    setRecipe(data.hits);
    console.log(data.hits);
  };

  // Use useEffect to detect changes in the recipe state and scroll to the recipe section
  useEffect(() => {
    if (recipe.length > 0 && recipeSectionRef.current) {
      recipeSectionRef.current.scrollIntoView({ behavior: "smooth" });
    }
  }, [recipe]);

  // Handle key down event to trigger getRecipe on Enter key press
  const handleKeyDown = (e) => {
    if (e.key === "Enter") {
      getRecipe();
    }
  };

  return (
    <div className="home">
      <div className="home-main">
        <div className="home-text">
          <h1>Find your Favourite Recipe</h1>
        </div>
        <div className="input-box">
          <span>
            <input
              type="text"
              placeholder="Enter Recipe"
              onChange={(e) => setQuery(e.target.value)}
              onKeyDown={handleKeyDown} // Add the onKeyDown event handler
            />
          </span>
          <IoSearch className="search-btn" onClick={getRecipe} />
        </div>
      </div>
      <div ref={recipeSectionRef} className="recipes">
        {recipe.map((item, index) => (
          <div key={index} className="recipe">
            <img
              className="recipe-img"
              src={item.recipe.image}
              alt={item.recipe.label}
            />
            <h2 className="label">{item.recipe.label}</h2>
            <Link to={`/recipe/${item.recipe.uri.split("_")[1]}`}>
              <button className="button">View Recipe</button>
            </Link>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Home;

Salin selepas log masuk
Trending.js

Halaman ini mengambil dan memaparkan resipi sohor kini.

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

const Trending = () => {
  const [trendingRecipes, setTrendingRecipes] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const API_ID = "2cbb7807";
  const API_KEY = "17222f5be3577d4980d6ee3bb57e9f00";

  useEffect(() => {
    const fetchTrendingRecipes = async () => {
      try {
        const response = await fetch(
          `https://api.edamam.com/api/recipes/v2?type=public&q=trending&app_id=${API_ID}&app_key=${API_KEY}`
        );
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        const data = await response.json();
        setTrendingRecipes(data.hits);
        setLoading(false);
      } catch (error) {
        setError("Failed to fetch trending recipes");
        setLoading(false);
      }
    };

    fetchTrendingRecipes();
  }, []);

  if (loading)
    return (
      <div className="loader-section">
        <div className="loader"></div>
      </div>
    );
  if (error) return <div>{error}</div>;

  return (
    <div className="trending-recipe">
      <div className="trending-recipe-main">
        <div className="trending-recipe-text">
          <h1>Trending Recipes</h1>
        </div>
      </div>

      <div className="recipes">
        {trendingRecipes.map((item, index) => (
          <div key={index} className="recipe">
            <img
              className="recipe-img"
              src={item.recipe.image}
              alt={item.recipe.label}
            />
            <h2 className="label">{item.recipe.label}</h2>
            <Link to={`/recipe/${item.recipe.uri.split("_")[1]}`}>
              <button className="button">View Recipe</button>
            </Link>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Trending;

Salin selepas log masuk
NewRecipe.js

Halaman ini mengambil Resipi Baharu dan memaparkan Resipi Baharu.

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

const NewRecipe = () => {
  const [newRecipes, setNewRecipes] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const API_ID = "2cbb7807";
  const API_KEY = "17222f5be3577d4980d6ee3bb57e9f00";

  useEffect(() => {
    const fetchNewRecipes = async () => {
      try {
        const response = await fetch(
          `https://api.edamam.com/api/recipes/v2?type=public&q=new&app_id=${API_ID}&app_key=${API_KEY}`
        );
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        const data = await response.json();
        setNewRecipes(data.hits);
        setLoading(false);
      } catch (error) {
        setError("Failed to fetch new recipes");
        setLoading(false);
      }
    };

    fetchNewRecipes();
  }, []);

  if (loading)
    return (
      <div className="loader-section">
        <div className="loader"></div>
      </div>
    );
  if (error) return <div>{error}</div>;

  return (
    <div className="new-recipe">
      <div className="new-recipe-main">
        <div className="new-recipe-text">
          <h1>New Recipes</h1>
        </div>
      </div>

      <div className="recipes">
        {newRecipes.map((item, index) => (
          <div key={index} className="recipe">
            <img
              className="recipe-img"
              src={item.recipe.image}
              alt={item.recipe.label}
            />
            <h2 className="label">{item.recipe.label}</h2>
            <Link to={`/recipe/${item.recipe.uri.split("_")[1]}`}>
              <button className="button">View Recipe</button>
            </Link>
          </div>
        ))}
      </div>
    </div>
  );
};

export default NewRecipe;

Salin selepas log masuk
Home.js

Halaman ini mengambil dan memaparkan halaman Utama dan resipi yang dicari.

import React, { useState, useRef, useEffect } from "react";
import { IoSearch } from "react-icons/io5";
import { Link } from "react-router-dom";


const Home = () => {
  const [query, setQuery] = useState("");
  const [recipe, setRecipe] = useState([]);
  const recipeSectionRef = useRef(null);

  const API_ID = "2cbb7807";
  const API_KEY = "17222f5be3577d4980d6ee3bb57e9f00";

  const getRecipe = async () => {
    if (!query) return; // Add a check to ensure the query is not empty
    const response = await fetch(
      `https://api.edamam.com/search?q=${query}&app_id=${API_ID}&app_key=${API_KEY}`
    );
    const data = await response.json();
    setRecipe(data.hits);
    console.log(data.hits);
  };

  // Use useEffect to detect changes in the recipe state and scroll to the recipe section
  useEffect(() => {
    if (recipe.length > 0 && recipeSectionRef.current) {
      recipeSectionRef.current.scrollIntoView({ behavior: "smooth" });
    }
  }, [recipe]);

  // Handle key down event to trigger getRecipe on Enter key press
  const handleKeyDown = (e) => {
    if (e.key === "Enter") {
      getRecipe();
    }
  };

  return (
    <div className="home">
      <div className="home-main">
        <div className="home-text">
          <h1>Find your Favourite Recipe</h1>
        </div>
        <div className="input-box">
          <span>
            <input
              type="text"
              placeholder="Enter Recipe"
              onChange={(e) => setQuery(e.target.value)}
              onKeyDown={handleKeyDown} // Add the onKeyDown event handler
            />
          </span>
          <IoSearch className="search-btn" onClick={getRecipe} />
        </div>
      </div>
      <div ref={recipeSectionRef} className="recipes">
        {recipe.map((item, index) => (
          <div key={index} className="recipe">
            <img
              className="recipe-img"
              src={item.recipe.image}
              alt={item.recipe.label}
            />
            <h2 className="label">{item.recipe.label}</h2>
            <Link to={`/recipe/${item.recipe.uri.split("_")[1]}`}>
              <button className="button">View Recipe</button>
            </Link>
          </div>
        ))}
      </div>
    </div>
  );
};

export default Home;


Salin selepas log masuk
Favorites.js

Halaman ini memaparkan resipi Kegemaran.

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";

const Favorites = () => {
  const [favorites, setFavorites] = useState([]);

  useEffect(() => {
    const savedFavorites = JSON.parse(localStorage.getItem("favorites")) || [];
    setFavorites(savedFavorites);
  }, []);

  if (favorites.length === 0) {
    return <div>No favorite recipes found.</div>;
  }

  return (
    <div className="favorites-page ">
      <div className="favorite-recipes-text">
        <h1>Favorite Recipes</h1>
      </div>

      <ul className="recipes">
        {favorites.map((recipe) => (
          <div className="recipe">
            <img className="recipe-img" src={recipe.image} alt={recipe.label} />
            <h2 className="label">{recipe.label}</h2>
            <Link to={`/recipe/${recipe.uri.split("_")[1]}`}>
              <button className="button">View Recipe</button>
            </Link>
          </div>
        ))}
      </ul>
    </div>
  );
};

export default Favorites;

Salin selepas log masuk
RecipeDetail.js

Halaman ini memaparkan resipi.

import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";

const RecipeDetail = () => {
  const { id } = useParams(); // Use React Router to get the recipe ID from the URL
  const [recipe, setRecipe] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [favorites, setFavorites] = useState([]);

  const API_ID = "2cbb7807";
  const API_KEY = "17222f5be3577d4980d6ee3bb57e9f00";

  useEffect(() => {
    const fetchRecipeDetail = async () => {
      try {
        const response = await fetch(
          `https://api.edamam.com/api/recipes/v2/${id}?type=public&app_id=${API_ID}&app_key=${API_KEY}`
        );
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        const data = await response.json();
        setRecipe(data.recipe);
        setLoading(false);
      } catch (error) {
        setError("Failed to fetch recipe details");
        setLoading(false);
      }
    };

    fetchRecipeDetail();
  }, [id]);

  useEffect(() => {
    const savedFavorites = JSON.parse(localStorage.getItem("favorites")) || [];
    setFavorites(savedFavorites);
  }, []);

  const addToFavorites = () => {
    const updatedFavorites = [...favorites, recipe];
    setFavorites(updatedFavorites);
    localStorage.setItem("favorites", JSON.stringify(updatedFavorites));
  };

  const removeFromFavorites = () => {
    const updatedFavorites = favorites.filter(
      (fav) => fav.uri !== recipe.uri
    );
    setFavorites(updatedFavorites);
    localStorage.setItem("favorites", JSON.stringify(updatedFavorites));
  };

  const isFavorite = favorites.some((fav) => fav.uri === recipe?.uri);

  if (loading)
    return (
      <div className="loader-section">
        <div className="loader"></div>
      </div>
    );
  if (error) return <div>{error}</div>;

  return (
    <div className="recipe-detail">
      {recipe && (
        <>
        <div className="recipe-details-text" >
          <h1>{recipe.label}</h1>
          <h2>Ingredients:</h2>
          <ul>
            {recipe.ingredientLines.map((ingredient, index) => (
              <li key={index}>{ingredient}</li>
            ))}
          </ul>
          <h2>Instructions:</h2>
          {/* Note: Edamam API doesn't provide instructions directly. You might need to link to the original recipe URL */}
          <p>
            For detailed instructions, please visit the{" "} 
            <a href={recipe.url} target="_blank" rel="noopener noreferrer">
              Recipe Instruction
            </a>

          </p>
          {isFavorite ? (
            <button className="fav-btn" onClick={removeFromFavorites}>Remove from Favorites</button>
          ) : (
            <button className="fav-btn" onClick={addToFavorites}>Add to Favorites</button>
          )}
        </div>
        <div className="recipe-details-img">
        <img src={recipe.image} alt={recipe.label} />
        </div>
        </>
      )}
    </div>

  );
};

export default RecipeDetail;

Salin selepas log masuk
Contact.js

Halaman ini memaparkan halaman Kenalan.

import React, { useState } from 'react';

const Contact = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  const [showPopup, setShowPopup] = useState(false);

  const handleSubmit = (e) => {
    e.preventDefault();

    // Prepare the contact details object
    const contactDetails = { name, email, message };

    // Save contact details to local storage
    const savedContacts = JSON.parse(localStorage.getItem('contacts')) || [];
    savedContacts.push(contactDetails);
    localStorage.setItem('contacts', JSON.stringify(savedContacts));

    // Log the form data
    console.log('Form submitted:', contactDetails);

    // Clear form fields
    setName('');
    setEmail('');
    setMessage('');

    // Show popup
    setShowPopup(true);
  };

  const closePopup = () => {
    setShowPopup(false);
  };

  return (
    <div className="contact">
      <h1>Contact Us</h1>
      <form onSubmit={handleSubmit} className="contact-form">
        <div className="form-group">
          <label htmlFor="name">Name:</label>
          <input
            type="text"
            id="name"
            value={name}
            onChange={(e) => setName(e.target.value)}
            required
          />
        </div>
        <div className="form-group">
          <label htmlFor="email">Email:</label>
          <input
            type="email"
            id="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div className="form-group">
          <label htmlFor="message">Message:</label>
          <textarea
            id="message"
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            required
          ></textarea>
        </div>
        <button type="submit">Submit</button>
      </form>

      {showPopup && (
        <div className="popup">
          <div className="popup-inner">
            <h2>Thank you!</h2>
            <p>Your message has been submitted successfully.</p>
            <button onClick={closePopup}>Close</button>
          </div>
        </div>
      )}
    </div>
  );
};

export default Contact;

Salin selepas log masuk
About.js

Halaman ini memaparkan Halaman Perihal.

import React from 'react';

const About = () => {
  return (
    <div className="about">
      <div className="about-main">
        <h1>About Us</h1>
        <p>
          Welcome to Recipe Finder, your go-to place for discovering delicious recipes from around the world!
        </p>
        <p>
          Our platform allows you to search for recipes based on your ingredients or dietary preferences. Whether you're looking for a quick meal, a healthy option, or a dish to impress your friends, we have something for everyone.
        </p>
        <p>
          We use the Edamam API to provide you with a vast database of recipes. You can easily find new recipes, view detailed instructions, and explore new culinary ideas.
        </p>
        <p>
          <strong>Features:</strong>
          <ul>
            <li>Search for recipes by ingredient, cuisine, or dietary restriction.</li>
            <li>Browse new and trending recipes.</li>
            <li>View detailed recipe instructions and ingredient lists.</li>
            <li>Save your favorite recipes for quick access.</li>
          </ul>
        </p>
        <p>
          Our mission is to make cooking enjoyable and accessible. We believe that everyone should have the tools to cook great meals at home.
        </p>
      </div>
    </div>
  );
};

export default About;

Salin selepas log masuk

Demo Langsung

Anda boleh melihat demo langsung projek di sini.

Kesimpulan

Tapak Web Pencari Resipi ialah alat yang berkuasa untuk sesiapa sahaja yang ingin menemui resipi baharu dan sohor kini. Dengan memanfaatkan React untuk bahagian hadapan dan API Edamam untuk data, kami boleh memberikan pengalaman pengguna yang lancar. Anda boleh menyesuaikan lagi projek ini dengan menambahkan ciri seperti penomboran, pengesahan pengguna atau pilihan penapisan yang lebih terperinci.

Jangan ragu untuk mencuba projek itu dan jadikan projek itu milik anda!

Kredit

  • API: Edamam
  • Ikon: Ikon React

Pengarang

Abhishek Gurjar ialah pembangun web berdedikasi yang bersemangat untuk mencipta aplikasi web yang praktikal dan berfungsi. Lihat lebih banyak projek beliau di GitHub.

Atas ialah kandungan terperinci Membina Laman Web Pencari Resipi menggunakan React. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan