React Router not linking to page

Viewed 144

Fairly new to react and trying to get the router to work for me. I'm pulling in data from a third party api using a simple form. When the data is retrieved I'd like to display it in a new route, and then ultimately have another route for each item retrieved. When I submit the form I just get a '?' in the route params. If I enter the route manually then submit the form the data displays. How can I get the data to display on form submit?

import axios from "axios";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Header from "./Header";

function App() {
  const [recipes, setRecipes] = useState([]);
  const [query, setQuery] = useState("");
  const [search, setSearch] = useState("");
  const APP_ID = "XXXXXXXX";
  const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
  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.preventDefault();
    setQuery(search);
    setSearch("");
  };
  
  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"
            />
            <button className="search-button" type="submit">
              Search Recipes
            </button>
          </form>
        </div>
      </div>
      {/* <RecipeList recipes={recipes}/> */}
      <Router>
        <Routes>
          <Route path="/recipes" element={<RecipeList recipes={recipes}/>} />
          <Route path="/recipes/:id" />
        </Routes>
      </Router>
    </div>
  );
}

export default App;

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

const RecipeList = ({ recipes }) => {
  console.log(recipes);
  return (
    <div>
      {recipes.map(({ recipe }, id) => (
        <Link to={`recipes/${recipe.label}`}>
          <p key={id}>{recipe.label}</p>
        </Link>
      ))}
    </div>
  );
};

export default RecipeList;
1 Answers

If I understand your question/issue you are having issue linking to a specific recipe. I suspect it is because you are using a relative link, so you are linking to a "/recipes/recipes/<label".

Either use absolute link paths, i.e. using a leading "/":

<Link to={`/recipes/${recipe.label}`}>
  <p key={id}>{recipe.label}</p>
</Link>

Or use a correct relative path, i.e. only append the next level path segment, in other words, appending recipe.label to "/recipes":

<Link to={`${recipe.label}`}>
  <p key={id}>{recipe.label}</p>
</Link>

If you wanting to start on "/" and submit the form and navigate to "/recipes" then issue an imperative navigation after submitting the form. Import the useNavigate hook to issue the imperative navigation and move the Router to wrap the App component so the routing context is provided to it and the useNavigate hook can work properly.

import axios from "axios";
import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom";
import "./App.css";
import RecipeList from "./RecipeList";
import Header from "./Header";

function App() {
  const navigate = useNavigate(); // <-- use navigate hook

  const [recipes, setRecipes] = useState([]);
  const [query, setQuery] = useState("");
  const [search, setSearch] = useState("");

  const APP_ID = "XXXXXXXX";
  const APP_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
  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.preventDefault();
    setQuery(search);
    setSearch("");
    navigate("/recipes"); // <-- imperative navigation
  };
  
  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"
            />
            <button className="search-button" type="submit">
              Search Recipes
            </button>
          </form>
        </div>
      </div>
      <Routes>
        <Route path="/recipes" element={<RecipeList recipes={recipes}/>} />
        <Route path="/recipes/:id" />
      </Router>
    </div>
  );
}

index.js

<Router>
  <App />
</Router>
Related