I'm learning react with strapi.
I have elements in a database and I manage to display them all without any problem.
I would like that when we click on an element we see the information of this one.
So I retrieve the link id without problem and I manage to display the information except that it only works once. In fact, if I click on the second element I have an error message.
Here is the code
const Articles = ({animal}) => {
const [error, setError] = useState(null);
const [Animaux, setAnimaux] = useState([]);
const { id } = useParams()
useEffect(() => {
axios
.get(`http://localhost:1337/api/animaux/`+id)
.then(({ data }) => setAnimaux(data))
.catch((error) => setError(error))
}, [id])
if (error) {
// Print errors if any
return <div>An error occured: {error.message}</div>;
}
console.log(Animaux)
return(
<div>
Id : {Animaux.data.id}
Nom : {Animaux.data.attributes.nom}
Description : {Animaux.data.attributes.Description}
<div key={animal.id} className="card" >
<span className="btn btn-primary">Voir {animal.attributes.nom}</span>
</div>
)}
*/}
</div>
)
};
export default Articles;
Here is the result the first time :
Here is the result the first time
The result that I have afterwards (after updating, then it does not come back at all)
The result after
I see that it returns me an empty array but I don't understand why
Many thanks to all for your future help.
Data which you get from api returns empty array. In addition, you cannot access the object of the array without giving an index.
You can't use like that {Animaux.data.id}, you can return with map ;
{
Animaux.map(function(data,index){
return(
<>
<p> id : {data.id} </p>
<p> nom : {data.nom} </p>
<p> Description : {data.Description} </p>
</>
)
})
}
Can you trying using the optional chaining operator.
return(
<div>
Id : {Animaux?.data?.id}
Nom : {Animaux?.data?.attributes?.nom}
Description : {Animaux?.data?.attributes?.Description}
<div key={animal?.id} className="card" >
<span className="btn btn-primary">Voir {animal.attributes.nom}</span>
</div>
</div>
Related
export default function Page({ data1 }) {
const [bookmark, setBookmark] = useState(
typeof window !== 'undefined'
? JSON.parse(localStorage.getItem('bookmark'))
: []
);
const addToBookmark = (ayatLs) => {
setBookmark([...bookmark, ayatLs]);
};
useEffect(() => {
localStorage.setItem('bookmark', JSON.stringify(bookmark));
}, [bookmark]);
return (
<>
<div className="modal-body">
<ul>
{bookmark.map((x) => (
<li key={x.nomor}>{x.tr}</li>
))}
</ul>
</div>
</>
);
}
{data1.map((x)) => (
<div className="pb-6 flex justify-between">
<span
onClick={() => addToBookmark(x)}
className={`text-sm `}>
{x.tr}
</span>
</div>
)}
When i use typeof window !== 'undefined' the Error: Hydration failed because the initial UI does not match what was rendered on the server in my code like this.
and when i change to localStorage.getItem('bookmark') the error is localStorage is not defined
when i click addToBookmark it will store data from props data1 to localStorage, no problem here, but when I fetch the data earlier in localStorage and I want to map the data that error appears
What's wrong with my code, why can't I map data from localStorage, please help me solve this problem.
The issue is that Next.js pre-renders pages by default, which means they are rendered on the server first and then sent to the client to be hydrated.
The error comes when you're setting the default value of the state based on this condition: typeof window !== 'undefined'.
Consider the following example:
const Page = () => {
const [name, setName] = useState(typeof window !== 'undefined' ? 'Peter' : 'Rick')
return <h1>{name}</h1>
}
export default Page
This code will throw an error of the type Error: Text content does not match server-rendered HTML.
If you inspect the page's source, you will see that the name rendered on the server was "Rick" while on the client-side's first render the name rendered was "Peter". There must be a match between the server-side rendered content and the client-side first render's content.
What you can do is move the localStorage data gathering and parsing logic to another useEffect instead and set the state in there. This solves the issue because useEffect only runs on the client-side, therefore you will effectively match both server-side and client-side first render content.
export default function Page({ data1 }) {
const [bookmark, setBookmark] = useState([])
const addToBookmark = (ayatLs) => {
setBookmark([...bookmark, ayatLs])
}
useEffect(() => {
if (bookmark.length === 0) return
localStorage.setItem('bookmark', JSON.stringify(bookmark))
}, [bookmark])
useEffect(() => {
const bookmarkFromLocalStorage = localStorage.getItem('bookmark')
const parsedBookmark =
bookmarkFromLocalStorage !== null
? JSON.parse(bookmarkFromLocalStorage)
: []
setBookmark(parsedBookmark)
}, [])
return (
<>
<div className='modal-body'>
<ul>
{bookmark.map((x) => (
<li key={x.nomor}>{x.tr}</li>
))}
</ul>
</div>
</>
)
}
I am fairly new to javascript and react, but I am trying to build this weather app, with both current and future weather.
import {React, useState} from "react"
import './weather.css'
const Weather = () => {
const apiKey = 'obsured-so-that-no-one-uses-my-key'
const [weatherData, setWeatherData] = useState([{}])
const [city, setCity] = useState("")
const [forcastWeather, setForcastWeather] = useState([{}])
const getWeather = (event) => {
if (event.key === "Enter") {
{fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`)
.then(response => response.json()).then(data => {setForcastWeather(data)})}
{fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`)
.then(response => response.json()).then(data => {setWeatherData(data)})}
}
}
return (
<>
<div className="container-about-weather">
<input className="input-about-weather"
placeholder="Search"
onChange={e => setCity(e.target.value)}
value={city}
onKeyPress={getWeather}>
</input>
<div>
{typeof weatherData.main === 'undefined' ? (
<div className="weather-content-rows-beginning">
<p>Welcome to my weather app. Enter a city name
to get the weather for that city
</p>
</div>
) : (
<div className="weather-all">
<div className="weather-now">
<div className="weather-content-rows-name">
<p>{weatherData.name}, {weatherData.sys.country}</p>
</div>
<div className="weather-content-rows-temp">
<p>{Math.round(weatherData.main.temp)}°C</p>
<img src={`https://openweathermap.org/img/wn/${weatherData.weather[0].icon}#2x.png`}
alt=""/>
</div>
<div className="weather-content-rows-description">
<p>{weatherData.weather[0].description}</p>
</div>
<div className="weather-content-humidity">
<p>Humidity: {weatherData.main.humidity}</p>
</div>
<div className="weather-content-wind">
<p>Wind: {(weatherData.wind.speed * 2.23694).toFixed(1)} mph</p>
</div>
</div>
<div className="weather-forcast">
<div className="weather-forcast-days">
<img src={`https://openweathermap.org/img/wn/${forcastWeather.list[5].weather[0].icon}#2x.png`}></img>
<p className="weather-forcast-days-text">{forcastWeather.list[5].main.temp}</p>
</div>
</div>
</div>
)}
</div>
</div>
</>
)
}
export default Weather
I seem to be to make the first api call and second api call (displaying the information) when I save the page.
However, when I switch page or reload, and try it again, the page goes blank until I reload it. I found an error in firefox saying "Uncaught TypeError: forcastWeather.list is undefined" and I am hoping this helps
I know this might be vague, but I was wondering if anyone could help me?
Overall there are a number of improvements you could add that will help! For starters, you have two async functions -
{fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apiKey}&units=metric`)
.then(response => response.json()).then(data => {setForcastWeather(data)})}
{fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`)
.then(response => response.json()).then(data => {setWeatherData(data)})}
But in your render, you're basing your decision of "do I have weather data" based only on the result of the first function -
{typeof weatherData.main === 'undefined' ? (...) : (...)}
What happens if the first async function passed but the second one failed? Or what if the second async function finishes much slower than the first?
One easy way to cater for this would be to create an explicit boolean to check that both async calls have finished, before you render; something like:
const hasWeatherData = !!(weatherData.main && forcastWeather.list?.length > 0);
return (
<>
{!hasWeatherData && (...)}
{hasWeatherData && (...)}
</>
);
This is not a perfect solution! It still doesn't cater for one of the calls failing, or one of your other assumptions failing (e.g. what if forcastWeather.list only has 3 elements in it? Then forcastWeather.list[5].main.temp would fail).
So, another idea would be to break your component apart into smaller components wiht smaller responsibilities. For example, you could break out the "forecast" part into a smaller component:
const WeatherForecast = ({ forecastWeather }) => {
const forecast = forecastWeather?.list?.length > 5
? forecastWeather.list[5]
: undefined;
if (!forecast) {
return (
<div>Forecast weather not found</div>
);
}
return (
<div className="weather-forcast">
<div className="weather-forcast-days">
<img src={`https://openweathermap.org/img/wn/${forecast.weather[0].icon}#2x.png`}></img>
<p className="weather-forcast-days-text">{forecast.main.temp}</p>
</div>
</div>
);
}
And then call that from the parent component -
return (
<>
{/* ----- 8< ----- */}
<WeatherForecast forecastWeather={forecastWeather} />
</>
);
the reason why your page is going black is the data is not there any more since you are calling them manually, in order to avoid such error you need to handle the case if those data are not available.
I've created a dynamic routing for a ProductDetail page (each Restaurant has its own details that should be shown on this page). the routing is working but I don't get any data and I can't figure out the right way to get data from firestore by the Id of each restaurant.
PS: the product details are rendering in the console but the problem still how to display to the detailpage
ProductDetail.js
import { firestore } from "../../../../../fire";
function ProductDetail() {
const { productId }= useParams();
const [product, setProduct] = useState();
useEffect( () => {
firestore
.collection("Restaurants")
.doc(productId).get()
.then( doc => {
console.log(doc.data())
setProduct(doc.data());
})
}, () => {
}
);
return (
<div className="col-12">
<div className="card">
<h1>{productId.name_restaurant} </h1>
<p>Price:${productId.Currency}</p>
<p>{productId.email} </p>
</div>
</div>
);
}
export default ProductDetail;
this my console : all details of the restaurant are returned
Still cannot return details on my page
As I can see your state name is product and the param name is productId. In the return you used productId which is incorrect.
As you are storing the data into the state. You should write product instead productId like below
<div className="col-12">
<div className="card">
<h1>{product?.name_restaurant} </h1>
<p>Price:${product?.Currency}</p>
<p>{product?.email} </p>
</div>
</div>
I'm currently working on a website using Strapi as a CMS and Next.js(React) in Frontend. The site also has an image slider which obviousely contains an image, a headline and a description. These three things I now want to get from my Strapi Collection Type called Banner (Banners). I've tried so many times and read through every forum I could find, but it somehow still doesn't work.
There's no error, but the data I want to display doesn't show up on the website. The following code is the one from \frontend\components\image-slider.js:
import React from "react";
const Slider = ({}) => {
const [banners, setBanners] = React.useState(null);
const getBanners = async() => {
const res = await fetch("http://localhost:1337/banners");
const json = await res.json();
setBanners(json);
}
if(!banners){
getBanners();
}
if (typeof window !== 'undefined') {
// Code for making slider work
}
return (
<div className="img-slider">
<div className="slide active">
<div className="info">
<div>
{banners ? banners.map((banner) => (
<div key={banner.id}>
<h2>{banner.title}</h2>
</div>
)) : (
<div>Loading...</div>
)}
</div>
<p>
{banners ? banners.map((banner) => (
<div key={banner.id}>
<h2>{banner.description}</h2>
</div>
)) : (
<div>Loading...</div>
)}
</p>
{/* <Image></Image>This I haven't tried yet. */}
</div>
</div>
<div className="slide">
{/* Same code as div before */}
</div>
{/* further slides */}
<div className="navigation">
<div className="btn-navig active"></div>
<div className="btn-navig"></div>
<div className="btn-navig"></div>
</div>
</div>
)
}
export default Slider;
The type of how I currently try to get the data is from this StackOverflow answer.
I hope someone is able to help me! Thanks a lot!
You have to put your API request (getBanners) in useEffect hook with [] as a dependencies (run only once - on load) like so:
React.useEffect(() => {
const getBanners = async() => {
const res = await fetch("http://localhost:1337/banners");
const json = await res.json();
setBanners(json);
}
getBanners();
}, [])
Please I don't know the reason why I'm getting '"URL of undefined" when accessing data in an API call? Presently, I'm working with an API in a react project and I needed to access an image URL in the data, Every other data I'm accessing works perfectly except the image URL.
codeSaandbox
Here is my working code except for the Image URL.
import React, { Component } from "react";
import axios from "axios";
export default class Cat extends Component {
state = {
data: [],
CatWeight: "",
CatMetric: ""
};
componentDidMount() {
this.fetchCountryData();
}
fetchCountryData = async () => {
const url = "https://api.thecatapi.com/v1/breeds";
try {
const response = await axios.get(url);
const data = await response.data;
this.setState({
data
});
const [CatWeight, CatMetric] = this.statistics();
this.setState({
CatWeight,
CatMetric
});
} catch (error) {
console.log(error);
}
};
render() {
return (
<div>
<h2>Cat Assignmaent</h2>
{console.log(this.state.data)}
{this.state.data.map((item, id) => {
return (
<div key={id}>
{/* <img src={item.image.url} alt=""/> Please why is this giving url undefined ? */}
<h2> {item.name}</h2>
<h3> {item.origin}</h3>
<h2>{item.temperament}</h2>
<h2>{item.life_span} kg</h2>
<p>{item.description}</p>
</div>
);
})}
</div>
);
}
}
simply because you are trying to access URL property at the moment your data does not exist yet. and as you know Javascript is Synchronous,
you could fix it by using Optional chaining
<img src={item?.image?.url} alt=""/>
check this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
While the explanation in the answer of #Swarup is correct, I would rather use this:
render() {
return (
<div>
<h2>Cat Assignmaent</h2>
{this.state.data.map((item, id) => {
return (
<div key={id}>
{
item.image
? <img src={item.image.url} alt={item.name} />
: 'No image, sorry'
}
<h2>{item.name}</h2>
<h3>{item.origin}</h3>
<h2>{item.temperament}</h2>
<h2>{item.life_span} kg</h2>
<p>{item.description}</p>
</div>
);
})}
</div>
);
}
Seems like this is an issue with the API. Some records don't image key in them. To verify this, add following lines after receiving response.
const filtered = data.filter(item => !item.image);
console.log(filtered);
To avoid this you can add a check before displaying an image.
{item?.image?.url && <><img src={item.image.url} alt=""/></> }
You are trying to access state data which is not exist yet, You must have initial state data that contains default URL for image, until you get image data from the API response, so by that you can avoid this problem,
and one thing i may suggest you need to have type safety for the response, so that you would know already what type of data you are going to get from the API response.