1

I have this App component which I want to get some data from my API on the back:

const App = () => { const [restaurants, setRestaurants] = useState(""); useEffect(() => { const fetchData = async () => { try { const response = await fetch( "http://localhost:3001/api/v1/restaurants" ); const data = await response.json(); console.log(data); setRestaurants(data); } catch (err) { console.log("Cant fetch"); } }; fetchData(); }, []); return ( // <RestaurantContextProvider> <div className="container"> <Router> <Routes> <Route exact path="/" element={<Home restaurants={restaurants} />} /> <Route exact path="/restaurants/:id/update" element={<UpdatePage />} /> <Route exact path="/restaurants/:id" element={<RestaurantDetailPage />} /> </Routes> </Router> </div> // </RestaurantContextProvider> ); }; export default App; 

If I change the element for a component in the Route element, it fetches but does not render. What is wrong? I did not find the answer on the V6 documentation of the Router.

Edited:

If i add the fetch inside this component, it fetch the data OK.

const RestaurantsList = (restaurants) => { return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {restaurants && restaurants.map((res) => ( <tr key={res.id}> <td>{res.name}</td> <td>{res.location}</td> <td>{"$".repeat(res.price_range)}</td> <td>Reviews</td> <td> <button className="btn btn-warning">Update</button> </td> <td> <button className="btn btn-danger">Delete</button> </td> </tr> ))} </tbody> </table> </div> ); }; export default RestaurantsList; 

Here it is the index.js:

import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; ReactDOM.render(<App />, document.getElementById("root")); 
17
  • I mean... maybe there's an error o the fetch as you are not updating the satate if theres an error? Commented Dec 15, 2021 at 13:47
  • I dont understand. If i fetch the data in a children, it works OK Commented Dec 15, 2021 at 13:50
  • did you try the render props of route ? <Route render={(routerProps => <Home restaurants={restaurants} {...routerProps} /> Commented Dec 15, 2021 at 13:50
  • You need to pass restaurants as dependency in useEffect hook. Based on dependency, rerender happens. Also use empty array instead of empty string in useState. Commented Dec 15, 2021 at 13:51
  • 2
    @miouri your fetchData should return a promise. The way you wrote the method it's not following best practice. You can declare fetchData outside of useEffect and pass the method name as dependency in useEffect. Also you can use useCallback to wrap up fetchData for memoised version. For now you give a try like this useEffect(() => { fetchData().then((data) => setRestaurants(data)); }, []); Commented Dec 15, 2021 at 14:06

1 Answer 1

1

You have likely successfully called fetchData in the mounting useEffect hook in App, and are passing the restaurants state to Home on a restaurants prop:

<Route path="/" element={<Home restaurants={restaurants} />} /> 

But in the routed component you are not accessing the restaurants prop at all, but instead have named the props object restaurants and are trying to map it. React props objects, if assigned to an argument, are always defined, but is an object, not an array.

const RestaurantsList = (restaurants) => { // <-- props object is restaurants return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {restaurants && restaurants.map((res) => ( <tr key={res.id}> <td>{res.name}</td> <td>{res.location}</td> <td>{"$".repeat(res.price_range)}</td> <td>Reviews</td> <td> <button className="btn btn-warning">Update</button> </td> <td> <button className="btn btn-danger">Delete</button> </td> </tr> ))} </tbody> </table> </div> ); }; 

To resolve:

  1. Access restaurants.restaurants to get to the actual restaurants prop that is an array to be mapped.

    const RestaurantsList = (restaurants) => { return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {restaurants.restaurants.map((res) => ( <tr key={res.id}> ... </tr> ))} </tbody> </table> </div> ); }; 

    By convention though, you should name the props object props so it's more clear what it is and doesn't lead to the confusion you have.

    const RestaurantsList = (props) => { return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {props.restaurants.map((res) => ( <tr key={res.id}> ... </tr> ))} </tbody> </table> </div> ); }; 
  2. Use props destructuring assignment to get to the restaurants prop.

    const RestaurantsList = (props) => { const { restaurants } = props; return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {restaurants.map((res) => ( <tr key={res.id}> ... </tr> ))} </tbody> </table> </div> ); }; 

    or

    const RestaurantsList = ({ restaurants }) => { return ( <div className="list-group"> <table className="table table-hover table-dark"> <tbody> {restaurants.map((res) => ( <tr key={res.id}> ... </tr> ))} </tbody> </table> </div> ); }; 

In App component, please ensure you maintain an array state invariant so the restaurants prop in children is always a valid array and mappable by using an empty array [] as the initial state over an empty string ''.

const [restaurants, setRestaurants] = useState([]); 
Sign up to request clarification or add additional context in comments.

6 Comments

for some reason is not working with your solution.... if I try to console.log to "debug" the state restaurants on the app, it does not get nothing.
@miouri What are where in App are you logging to debug? Is the request made, do you see it in your network tab in the browser's devtools? If so, what is the value of console.log(data); logged in the useEffect's fetchData function?
i´m console.log inside the fetch function and after setting the state: useEffect(() => { const fetchData = async () => { try { const response = await fetch( "localhost:3001/api/v1/restaurants" ); const data = await response.json(); setRestaurants(data); console.log(restaurants); } catch (err) { console.log("Cant fetch"); } }; fetchData(); }, [restaurants]);
Maybe is something with Router? I´m using V6. I updated the code with the index.js, pls look at it.
@miouri React state updates are asynchronously processed, you can't log the restaurants state right after enqueueing an update as it will log the state value from the current render cycle, not what it will be in a subsequent render cycle after updating. Use useEffect(() => console.log(restaurants), [restaurants]); to log state updates.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.