unable to map as url is not an array // React Router - javascript

Hi I have a main page called FeaturedProduct.js which lists all the products fetch from the API https://fakestoreapi.com.
I trying to set up react router dom version (6) whereby user click on any of the product will open up that single product through Product.js
This is my code: https://codesandbox.io/s/magical-smoke-r7yik9?file=/src/Product.js
I having issues because I can't use the map function without errors.
The error being `data.map' is a not a function (in Product.js)
Do I need to access further into the "api" json like data.x.map?

Always check carefully the response you are getting from an api weather its object or an array.
i have modified your file, you can replace it with your original product.js component.
And always use ?. operator whenever you play with data that come from an api because useEffect hook will run after your component is loaded, hence it will give you an error that your data is undefined. It will not happen in your case you are getting only single object which is fast operation, but if you are getting larger data then you have to use ?. operator.
import React, { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import axios from "axios";
// individual product
// functional component
const Product = () => {
const [data, setData] = useState('');
const { id } = useParams();
useEffect(() => {
axios
.get(`https://fakestoreapi.com/products/${id}`) // change from ?id=${id} to ${id} because API url is .com/products/1 // But couldn't map due to not being array
.then((res) => {
setData(res.data);
})
.catch((err) => console.log(err));
}, []);
return (
<div>
<div className="product-container" key={data?.id}>
<div>
<img className="prod-image" src={data?.image} alt="" />
</div>
<div>
<h1 className="brand">{data?.title}</h1>
<h2>{data?.category}</h2>
<p>{data?.description}</p>
<p>
<strong>Price:</strong> {data?.price}
</p>
<p>
<strong>Rating:</strong> {data?.rating?.rate}
</p>
</div>
</div>
<div className="back">
<Link to="/">Feature Products</Link>
</div>
</div>
);
};
export default Product;

Related

Why does my React app disappear when I run it

Whenever I get data on my page, after a few seconds, my whole react app disappears as in the root div in the html is left completely empty like this <div id="root"></div> as if there is nothing. This is happening on all my other projects too even when I create a new one, this disappearing of the react keeps happening sometimes even without adding any logic, it refuses to render plain html. The errors I get for now on this current project on the console is this
characters.map is not a function
I know not what could be causing this but my code looks like this for now starting with the App.js file. I am extracting data from an api.
import {BrowserRouter, Route, Routes} from "react-router-dom"
import Home from "./components/Home";
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
And then followed by the CharactersListing page which is supposed to render all the characters of the show
import React, {useEffect, useState} from 'react'
import CharacterCard from './CharacterCard'
export default function BadListings() {
const [characters, setCharacters] = useState([])
useEffect(() => {
const getData = async () => {
await fetch("https://www.breakingbadapi.com/api/characters")
.then(response => {
setCharacters(response.json());
console.log(characters);
})
.catch(err => console.log("There must have been an error somewhere in your code", err.message));
}
getData();
});
return (
<div className='container'>
{characters.map(character => (
<div>
<CharacterCard name={character.name} status={character.status} image={character.img} />
</div>
))}
</div>
)
}
And finally, the CharacterCard.js
import React from 'react'
import "../styles/styles.css"
export default function CharacterCard({name, status, image}) {
return (
<div className='card'>
<h1>{name}</h1>
<h2>{status}</h2>
<img src={image} alt="umfanekiso" className='imgur' />
</div>
)
}
I do not know what could be causing this. I have never had this issue it just started today. What could be causing it
Issues
The issue is that you are not setting the characters state to what you think it is. response.json() returns a Promise object and doesn't have a map property that is a function to be called.
The useEffect hook is also missing a dependency, so anything that triggers this BadListings component to rerender will also retrigger this useEffect hook, which updates state and triggers another rerender. The code is likely render looping.
Solution
The code should wait for the response.json() Promise to resolve and pass that result value into the characters state updater function. Note that I've also rewritten the logic to use async/await with try/catch as it is generally considered anti-pattern to mix async/await with Promise chains.
Add a dependency array to the useEffect hook. Since I don't see any dependencies use an empty array so the effect runs only once when the component mounts.
Promise chain Example:
useEffect(() => {
fetch("https://www.breakingbadapi.com/api/characters")
.then(response => response.json()) // <-- wait for Promise to resolve
.then(characters => setCharacters(characters)
.catch(err => {
console.log("There must have been an error somewhere in your code", err.message)
});
}, []); // <-- add empty dependency array
async/await Example:
useEffect(() => {
const getData = async () => {
try {
const response = await fetch("https://www.breakingbadapi.com/api/characters");
const characters = await response.json(); // <-- wait for Promise to resolve
setCharacters(characters);
} catch(err) {
console.log("There must have been an error somewhere in your code", err?.message);
};
}
getData();
}, []); // <-- add empty dependency array
Don't forget to add a React key to the mapped characters:
{characters.map((character) => (
<div key={character.char_id}> // <-- Add React key to outer element
<CharacterCard
name={character.name}
status={character.status}
image={character.img}
/>
</div>
))}
characters is a string and strings don't have .map() method, that's why React is crashing. And since React's crashed, it couldn't mount generated HTML to the #root.
You can use [...strings] to use .map() method.
You are exporting CharacterCards and not BadCards.
Please change all BadCards in your CharactersListing page to CharacterCards
There is an explaination What does "export default" do in JSX?
Great instinct to look for errors in the console.
Umut Gerçek's answer is correct, but I'd add an additional suggestion: if you're going to map over something, you should instantiate it in state as a thing that can be mapped. Set its initial state to an array:
const [characters, setCharacters] = useState([])
Note the capital 'C' in the setter; that is the useState convention.
Then set characters as you're currently doing:
setCharacters(response.json());
And your map should work regardless of the result of your fetch, and handle multiple items, too.

React component rendering multiple times, failing when reloading the page

I have a rails (7.0.2) application and just installed React. I'm very new to react and can't seem to understand why it looks like my component is loading multiple times, the first time with an empty value for props and the second time with the correct values for props.
App.js:
import "./App.css";
import axios from "axios";
import Customers from "./components/customers";
import { useEffect, useState } from "react";
const API_URL = "http://localhost:3000/internal_api/v1/customers";
function getAPIData() {
return axios.get(API_URL).then((response) => response.data);
}
function App() {
const [customers, setCustomers] = useState([]);
useEffect(() => {
let mounted = true;
getAPIData().then((items) => {
if (mounted) {
setCustomers(items);
}
});
return () => (mounted = false);
}, []);
console.log('LOADED App.js');
return (
<div className="App">
<h1>Hello</h1>
<Customers customers={customers} />
</div>
);
}
export default App;
and customers.js:
import React from "react";
function Customers(props) {
console.log('LOADED customers.js');
return (
<div>
<h1>These customers are from the API</h1>
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
})}
</div>
);
}
export default Customers;
When I remove this part of the code and reload the page, my props come through correctly when looking in console. Then, when I put the code back and save (without reloading), it displays correctly.
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
However, as soon as I reload again, I get the same following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
It seems as though the first time everything renders, props is empty. Then the second time, it is full with the data. I checked my rails app and it only hits the API once. What am I doing wrong?
More log outputs:
React component rendering multiple times?
React will render fast before completing the request in use Effect
so in first render customers array will be empty
when request is fulfilled, you are changing state, So react will re-render the component
Only component that uses state reloads when the state is changed this is required else UI will not update
failing when reloading the page? | Failed on Initial Load
Since in Initial render customers will have no data customers.data will be undefined so it will not have map
to bypass this error use props.customers?.data && props.customers.data?.map() addding question mark means expression will be evaluated if not undefined
Source - Optional_chaining

How does useState set variables? [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I've posted something similar in the past and never really got an answer and I keep running into this problem. Things in my app are working, but I'm not really sure why. I'm using a third-party api to make a simple recipe app. The data I'm passing into my component is displaying as expected, but it's not logging into the console as expected.
Line 20 of App.js is logging what I'd expect (the data received from the api).
Line 4 of Recipe.jsx is logging what I'd expect (each item in the recipes array).
But line 22 of App.js is coming back as undefined. I would expect that because I setRecipes to 'data', that 'recipes' would log the same as 'data'. Can anyone explain this to me?
import React, { useEffect, useState } from "react";
import "./App.css";
import Recipe from "./Recipe";
import axios from "axios";
function App() {
const APP_ID = "XXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}`;
const [recipes, setRecipes] = useState([]);
useEffect(() => {
getRecipes();
}, []);
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
console.log(recipes);
};
return (
<div className="App">
<form className="search-form">
<input className="search-input" type="text" />
<button className="search-button" type="submit">
Search Recipes
</button>
</form>
{recipes &&
recipes.map((recipe, idX) => <Recipe recipe={recipe} id={idX} />)}
</div>
);
}
export default App;
import React from "react";
const Recipe = ({ recipe, id }) => {
console.log(recipe);
return (
<div key={id}>
<h1>{recipe.recipe.label}</h1>
<p>{recipe.recipe.calories}</p>
<img src={recipe.recipe.images.SMALL.url} alt="" />
</div>
);
};
export default Recipe;
SetState is asynchronous and may not resolve straightaway. See this part of the docs for more information
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
As you've said, the data coming back is correct, and you've made a call to set state. But the state hasn't resolved by the time you come to the next line where state is consoled out

How do I call an Axios response once and without a button

I am new to using react and Axios and I have created a get request, I can call it once with a button, however I don't want this button and instead want information to be displayed when the page loads/with the page so the user can see it straight away. But when calling my function once it gets called continuously and crashes the web browser and I don't understand why this is happening I have googled and I couldn't find anything. Here is the code that gets ran.
kitchen.js
import React from 'react';
import { Container } from 'react-bootstrap';
// import Axios from 'axios';
import { Link } from 'react-router-dom';
import GetFood from './getFood';
export default function Kitchen() {
return(
<Container>
<div>
<h1>This is the kitchen portal</h1>
<Link to='/gettingfood'><button>Get Food</button></Link>
<Link to="/addingfood"><button>Add food</button></Link>
<Link to="/deletefood"><button>Delete Food</button></Link>
</div>
<GetFood/>
</Container>
);
}
GetFood.js
import React, { useState } from 'react';
import Axios from 'axios';
export default function GetFood() {
const [responseData, setResponseData] = useState([])
// fetches data
async function fetchData(){
await Axios.get("http://localhost:3001/getfood").then((response)=>{
setResponseData(response.data);
console.log(response.data);
alert("Information received!")
})
.catch((error) => {
console.log(error)
})
}
fetchData();
return (
<div>
<button onClick={fetchData}>Get</button>
{responseData.map((val,key)=>{
return (
<div>
<div id="data">
<p>Item:{val.item}</p>
<p>Price:{val.price}</p>
</div>
</div>
)
})}
</div>
)
}
In React, functional components get called everytime they get rendered.
To create side-effects, like requesting data from an external source, you should use the useEffect hook.
This hook takes a function to execute and a dependency array, which defines when the supplied function gets called.
If you specify an empty array, the function only gets called on the first render cycle.
If you specify any variables, the function gets called on the first render cycle and when any of the specified variables change.
This should go instead of your call to fetchData():
useEffect(() => {
fetchData();
}, []);

How to display a particular component with different API data on different pages

I am new to react and there is this challenge that i am having,
I have slider created a component
import React from 'react'
import NextEvent from '../nextEvent/NextEvent'
import './slider.css';
function Slider(props) {
const {id, image, sub_title, title} = props;
return (
<main id='slider'>
<div className="slide" key= {id}>
<div className="slide-image">
<img src={image} alt="slider-background"/>
</div>
<h1>{title} </h1>
<h5>...{sub_title}</h5>
</div>
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
export default Slider
I need to have this banner component on almost all my pages, and on each of the pages, it comes with a
different information (image, title, subtitle)
the backend guy sent the api and i consumed, but the problem is that, if i consume the api on the
component directly, all the pages will have the same info on the banner component which is not what i want,
also consuming the API on the homepage seemed like it was not the right thing to do, so i created another component which
collects the Api and i then added that new component to my homepage.
now my question goes:
did i do the correct thing ?
if correct, does it mean i have to create new corresponding components that will receive the APi for
each page i want to display the banner just like i did for the homepage?
will i have to as the backend guy to create different apis for each of the pages in which the
component is to be displayed
if no please help me with an efficient way which i can inject data coming from the backend into a
component which will be displayed on different pages with different data
this is the new component i created for the APi consumption
import React, {useState, useEffect } from 'react'
import NextEvent from '../../components/nextEvent/NextEvent'
import axios from "axios";
import '../../components/slider/slider.css';
const sliderUrl = "*************************************"
function HomeSlider(props) {
const [sliderData, setSliderData] = useState([]);
const { image, sub_title, title} = props;
const getSliderContents = async () => {
const response = await axios.get(sliderUrl);
const content = response.data;
setSliderData(content);
}
useEffect(() => {
getSliderContents();
}, [])
// console.log("slider", sliderData)
if(sliderData) {
return (
<main id='slider'>
{sliderData.map((item) => {
return (
<div className="slide" key= {item.id}>
<div className="slide-image">
<img src={item.image} alt="slider-background"/>
</div>
<h1>{item.title} </h1>
<h5>...{item.sub_title}</h5>
</div>
)
})}
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
}
export default HomeSlider
this is the Homepage i displayed it
function HomePage() {
return (
<div>
<NavBar/>
<SecondaryMenu/>
<HomeSlider />
<FeaturedBox />
Please any help is appreciated, i have search all over but no one explains how to display
component with different data on different pages
So i just wanted to get back on this, i figured i have to setup a service point where i call the api and then consume the endpoints on each page as desired

Categories

Resources