Heim  >  Artikel  >  Web-Frontend  >  Erstellen einer Rezeptfinder-Website mit React

Erstellen einer Rezeptfinder-Website mit React

DDD
DDDOriginal
2024-09-13 12:33:301167Durchsuche

Building Recipe Finder Website using React

Einführung

In diesem Blog erstellen wir mit React eine Recipe Finder-Website. Mit dieser App können Benutzer nach ihren Lieblingsrezepten suchen, aktuelle oder neue Rezepte anzeigen und ihre Lieblingsrezepte speichern. Wir werden die Edamam-API nutzen, um Rezeptdaten in Echtzeit abzurufen und sie dynamisch auf der Website anzuzeigen.

Projektübersicht

Der Rezeptfinder ermöglicht Benutzern Folgendes:

  • Rezepte nach Namen suchen.
  • Angesagte und neu hinzugefügte Rezepte anzeigen.
  • Detaillierte Informationen zu einzelnen Rezepten anzeigen.
  • Fügen Sie Rezepte zu einer Favoritenliste hinzu und speichern Sie die Daten mithilfe von localStorage.

Merkmale

  • Suchfunktion: Benutzer können nach Rezepten suchen, indem sie eine Suchanfrage eingeben.
  • Trendrezepte: Zeigt aktuell angesagte Rezepte aus der API an.
  • Neue Rezepte: Zeigt die neuesten Rezepte aus der API an.
  • Rezeptdetails: Zeigt detaillierte Informationen zu einem ausgewählten Rezept an.
  • Favoriten: Ermöglicht Benutzern das Hinzufügen von Rezepten zu einer Favoritenliste, die lokal gespeichert wird.

Verwendete Technologien

  • Reagieren: Zum Erstellen der Benutzeroberfläche.
  • React Router: Für die Navigation zwischen verschiedenen Seiten.
  • Edamam API: Zum Abrufen von Rezepten.
  • CSS: Zum Gestalten der Anwendung.

Projektstruktur

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

Installation

Um dieses Projekt lokal auszuführen, befolgen Sie diese Schritte:

  1. Klonen Sie das Repository:
   git clone https://github.com/abhishekgurjar-in/recipe-finder.git
   cd recipe-finder
  1. Installieren Sie die Abhängigkeiten:
   npm install
  1. Starten Sie die React-App:
   npm start
  1. Besorgen Sie sich Ihre Edamam API-Anmeldeinformationen (API-ID und API-Schlüssel) von der Edamam-Website.

  2. Fügen Sie Ihre API-Anmeldeinformationen auf den Seiten hinzu, auf denen API-Aufrufe durchgeführt werden, z. B. Home.js, Trending.js, NewRecipe.js und RecipeDetail.js.

Verwendung

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;

Home.js

Dies ist die Hauptseite, auf der Benutzer mithilfe der Edamam-API nach Rezepten suchen können.

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;

Trending.js

Diese Seite ruft Trendrezepte ab und zeigt sie an.

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;

NewRecipe.js

Diese Seite ruft NewRecipe ab und zeigt neue Rezepte an.

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;

Home.js

Diese Seite ruft die Startseite und das gesuchte Rezept ab und zeigt sie an.

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;


Favorites.js

Auf dieser Seite werden Lieblingsrezepte angezeigt.

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;

RecipeDetail.js

Auf dieser Seite werden die Rezepte angezeigt.

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;

Contact.js

Auf dieser Seite wird die Kontaktseite angezeigt.

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;

About.js

Auf dieser Seite wird die Seite „Info“ angezeigt.

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;

Live-Demo

Die Live-Demo des Projekts können Sie hier ansehen.

Abschluss

Die Recipe Finder-Website ist ein leistungsstarkes Tool für alle, die neue und trendige Rezepte entdecken möchten. Durch die Nutzung von React für das Frontend und der Edamam-API für Daten können wir ein nahtloses Benutzererlebnis bieten. Sie können dieses Projekt weiter anpassen, indem Sie Funktionen wie Paginierung, Benutzerauthentifizierung oder noch detailliertere Filteroptionen hinzufügen.

Fühlen Sie sich frei, mit dem Projekt zu experimentieren und es zu Ihrem eigenen zu machen!

Credits

  • API: Edamam
  • Symbole: Reaktionssymbole

Autor

Abhishek Gurjar ist ein engagierter Webentwickler, der sich mit der Entwicklung praktischer und funktionaler Webanwendungen beschäftigt. Schauen Sie sich weitere seiner Projekte auf GitHub an.

Das obige ist der detaillierte Inhalt vonErstellen einer Rezeptfinder-Website mit React. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn