Fairly new to react here. I'm making a small recipe finder app with an api. After getting the data, I'm mapping through the results and displaying them in a component. What I want to do is display the details of each recipe through another component in another route. I'm not sure how to do this. I thought I could pass the mapped recipe through Link, but it's not working. Here is what I have so far.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React, { useState, useEffect} from "react";
import axios from "axios";
import { BrowserRouter as Router, Routes, Route, useNavigate} from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Recipe from "./Recipe";
import Header from "./Header";
function App() {
const navigate = useNavigate();
const [recipes, setRecipes] = useState([]);
const [query, setQuery] = useState("");
const [search, setSearch] = useState("");
const APP_ID = "XXXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`;
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
};
useEffect(() => {
getRecipes();
}, [query]);
const updateSearch = (e) => {
setSearch(e.target.value);
console.log(search);
};
const getSearchQuery = (e) => {
e.preventDefault();
setQuery(search);
setSearch("");
navigate("/recipes");
};
return (
<div className="App">
<Header />
<div>
<div className="container">
<form className="search-form" onSubmit={getSearchQuery}>
<input
className="search-input"
type="text"
value={search}
onChange={updateSearch}
placeholder="search by food name"
/>
</form>
</div>
</div>
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes} />} />
<Route path="/recipes/:id" element={<Recipe recipes={recipes} />}/>
</Routes>
</div>
);
}
export default App;
RecipeList.jsx
import React from "react";
import { Link } from "react-router-dom";
const RecipeList = ({ recipes }) => {
return (
<div className="container">
<div className="grid-container">
{recipes.map(({ recipe }) => (
<Link to={`/recipes/${recipe.label}`}>
<img key={recipe.image} src={recipe.image} alt="" />
<p key={recipe.label}>{recipe.label}</p>
<p>{recipe.id}</p>
</Link>
))}
</div>
</div>
);
};
export default RecipeList;
Recipe.jsx
const Recipe = ({recipe}) => {
return (
<div>
<h1>{recipe.label}</h1>
</div>
)
}
export default Recipe
Am I even close???
CodePudding user response:
You are passing the entire recipes
array to both routed components.
<Routes>
<Route path="/recipes" element={<RecipeList recipes={recipes} />} />
<Route path="/recipes/:id" element={<Recipe recipes={recipes} />}/>
</Routes>
So Recipe
can use the entire array and the id
route match param to search the passed array and render the exact recipe by matching label
.
import { useParams } from 'react-router-dom';
const Recipe = ({ recipes }) => {
const { id } = useParams();
const recipe = recipes.find(recipe => recipe.label === id); // *
return recipe ? (
<div>
<h1>{recipe.label}</h1>
</div>
) : null;
};
* Note: Since you call the route param id
it may make more sense to us the recipe.id
for the link.
{recipes.map(({ recipe }) => (
<Link to={`/recipes/${recipe.id}`}>
<img key={recipe.image} src={recipe.image} alt="" />
<p key={recipe.label}>{recipe.label}</p>
<p>{recipe.id}</p>
</Link>
))}
...
const recipe = recipes.find(recipe => recipe.id === id);