Not getting any data from getStaticProps - javascript

I'm working on a next.js project and I'm trying to get the list of gyms from getStaticProps like this:
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import { useState } from "react";
import { server } from '../config';
export default function Map ({gyms}){
console.log("data = "+ JSON.stringify(gyms));
return (
<MapContainer>
<TileLayer url={`I deleted the url`} attribution='Nothing to see here' />
{gyms.map((gym, index) => {
return (
<Marker
key={index}
position={[gym.address1, gym.address2]}
draggable={true}
animate={true}
>
</Marker>
);
})}
</MapContainer>
);
}
export async function getStaticProps() {
const res = await fetch(`${server}/api/gyms`)
const response = await res.json()
const gyms = response.gyms
console.log("gyms list = "+ gyms);
if (!gyms) {
return {
notFound: true,
}
}
return {
props: {
gyms,
// Will be passed to the page component as props
},
};
}
As you can see I have one console.log in the getStaticProps, that returns nothing, and another console log in the component that returns "data = undefined"
The error that I get is: TypeError: Cannot read properties of undefined (reading 'map')

The error you are getting is from the 'gyms' prop. Please make sure if you are using useEffect in the component where you are using Map, the array variable should be in the array. Since you are getting the array as undefined then this is the most possible problem. Just put it in the array like this:
useEffect(() => {
//Code goes here...
}, [gyms])

Related

props data null when component is rendered

This question does not show any research effort; it is unclear or not useful
I have created a generic component to be used in 2 cases. 1 case when dealing with single piece of data the other when dealing with an array of data. I am trying to plot this data on a react leaflet map. Right now it works for my landingPage component which deals with the single plots of data. Previously I had it also working for my array of data before I was passing props to generic component to render. The issue is when I try to load the page responsible for displaying the map with the array of data it returns null when the getInitPosition() function is called as the props data seems to be null when component is rendered but not null after it, I checked this through logging to console. I am confused as to how it works in the single component and not the array of data component as the calls to retrieve the data are very similar. Can anyone see where I am going wrong. It seems to be that although my polyineArray is set with correct values I then print out the polylines state to check if it is set after the call to setPolylines(polylineArray) but it seems to be empty and I do not know why? How can I ensure the polylines state is not empty before passing it as props
Map array of data component
import react from "react";
import { useState, useEffect } from "react";
import { MapContainer, TileLayer, Popup, Polyline } from "react-leaflet";
import axios from "axios";
import polyline from "#mapbox/polyline";
import MapComp from "./MapComp";
function Map() {
const [activities, setActivities] = useState([]);
const [polylines, setPolylines] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setActivitieData();
}, []);
useEffect(() => {
if (activities.length) {
setPolylineArray();
setIsLoading(false);
}
}, [activities]);
const getActivityData = async () => {
const response = await axios.get("http://localhost:8800/api");
return response.data;
};
const setActivitieData = async () => {
const activityData = await getActivityData();
setActivities(activityData);
};
const setPolylineArray = () => {
const polylineArray = [];
for (let i = 0; i < activities.length; i++) {
const polylineData = activities[i].map.summary_polyline;
const activityName = activities[i].name;
const activityType = activities[i].type;
polylineArray.push({
positions: polyline.decode(polylineData),
name: activityName,
activityType: activityType,
});
} // should push activity type as well
setPolylines(polylineArray);
//setIsLoading(false);
console.log("Polyline array = ", polylineArray);
console.log("polylines = ", polylines);
};
return !isLoading ? (
<MapComp activityData={{ polylines }} />
) : (
<div>
<p>Loading...</p>
</div>
);
}
export default Map;
generic map component
import react from "react";
import { MapContainer, TileLayer, Popup, Polyline } from "react-leaflet";
import polyline from "#mapbox/polyline";
import { useEffect, useState } from "react";
function MapComp(props) {
function getInitPosition() {
console.log("props activity data = ", props);
if (!Array.isArray(props.activityData)) {
return [
props.activityData.positions[0][0],
props.activityData.positions[0][1],
];
} else {
return [
props.activityData.poylines.positions[0][0],
props.activityData.poylines.positions[0][1],
];
}
}
return (
<MapContainer center={getInitPosition()} zoom={15} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{!Array.isArray(props.activityData) && (
<Polyline positions={props.activityData.positions}>
<Popup>
<div>
<h2>{"Name: " + +props.activityData.name}</h2>
</div>
</Popup>
</Polyline>
)}
{Array.isArray(props.activityData.polylines) &&
props.activityData.polylines.length > 1 &&
props.activityData.polylines.map((activity, idx) => (
<Polyline key={idx} positions={activity.positions}>
<Popup>
<div>
<h2>{"Name: " + activity.name}</h2>
</div>
</Popup>
</Polyline>
))}
</MapContainer>
);
}
export default MapComp;
try this on you common component
<MapComp activityData={polylines} />

React: How to avoid duplication in a state array

I am making MERN social media app.
I want to show all the friends of the current user in a list in SideBar.jsx .
Home.jsx (parent of Sidebar.jsx)
import React, { Component } from "react";
import { Person } from "#material-ui/icons";
import Topbar from "../../components/topbar/Topbar";
import Sidebar from "../../components/sidebar/Sidebar";
import Feed from "../../components/feed/Feed";
import Rightbar from "../../components/rightbar/Rightbar";
import "./home.css";
export default function Home() {
return (
<>
<Topbar />
<div className="homeContainer">
<Sidebar />
<Feed />
<Rightbar />
</div>
</>
);
}
SideBar.jsx
import "./sidebar.css";
import React, { Component, useContext, useState } from "react";
...
import { axiosInstance } from "../../config";
export default function Sidebar() {
const { user } = useContext(AuthContext);
const [followings, setFollowings] = useState([]);
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
} catch (error) {
console.log(error);
}
});
};
fetchFollowings();
}, [user]);
return (
<div className="sidebar">
.....
<ul className="sidebarFriendList">
{followings.map((u) => (
<CloseFriend key={u._id} user={u} />
))}
</ul>
</div>
</div>
);
}
For example, in this case, in the state "followings", there are 2 user objects.
So, the line
followings.map((u) => (...
should only show 2 entries.
However, the result is below.
As you can see, it is showing each friend twice.
I tired to check if a user object already exists in followings by doing
if (followings.includes(theUser.data)) {
} else {
setFollowings((prev) => [...prev, theUser.data]);
}
But this is not working.
How can I make sure that it only shows each user once?
I want it to be like this
Any help would be greatly appreciated. thank you
This is happening because it seems that your useEffect method is being fired two times (probably because you are using React.StrictMode) and you are setting the state inside the .map method (that is not good because you trigger a new render each time you call the setState).
What I would recommend you to do, is to remove the setState from the .map method and just set the new state after you format your data. So it would be something like this:
const newFollowings = followingsList.map(async (id) => {
try {
const theUser = await axiosInstance.get(`/users?userId=${id}`);
return theUser.data;
} catch (error) {
console.log(error);
}
});
setFollowings(newFollowings);
Probably you would have to add a filtering to the array in case there are some errors (because on errors the mapped value would be undefined):
.filter(data => data);
When you are using the .map function with async/await Promise.all usually always does the trick. Instead of pushing the state on every iteration you collect the followers list and set the state when all your fetching is done. I did not test it yet, but I hope it works.
const followingsList = user.followings;
useEffect(() => {
const fetchFollowings = async () => {
const list = await Promise.all(followingsList.map(async (id) => (
await axios.get('/user?userId=' + id);
)));
setFollowers(list);
};
fetchFollowings();
}, [user]);
Note: let me know if it works, if not I'll do a little sandbox on my own

How to await data coming from an API as a prop in a component?

I am trying to send a prop on a component once my data loads from an API. However, even though I am using async await to await from my data, I am still getting an error of: Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
My process:
Have two set states: 1) updateLoading and 2) setRecordGames
Once the data is loaded, I will set updateLoading to false and then set recordGames with the data from the API.
My Problem:
It seems that when I pass data in the component, React does not wait for my data load and gives me an error.
This is my code:
import useLoadRecords from '../../hooks/useLoadRecords'
import { CustomPieChart } from '../charts/CustomPieChart'
export const GameDistribution = () => {
const { records, loading } = useLoadRecords()
let data = records
console.log(data) // This logs out the records array
return (
<div>
// once loading is false, render these components
{!loading ? (
<>
<div>
{recordGames.length > 0 ? (
// This line seem to run as soon as the page loads and I think this is the issue. recordGames is empty at first, but will populate itself when the data loads
records.length > 0 && <CustomPieChart data={data} />
) : (
<div>
No games
</div>
)}
</div>
</>
) : (
<span>Loading...</span>
)}
</div>
)
}
// useLoadRecords.js
import { useState, useEffect } from 'react'
import { API } from 'aws-amplify'
import { listRecordGames } from '../graphql/queries'
// Centralizes modal control
const useLoadRecords = () => {
const [loading, setLoading] = useState(false)
const [records, updateRecords] = useState([])
useEffect(() => {
fetchGames()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const fetchGames = async () => {
try {
let recordData = await API.graphql({
query: listRecordGames,
})
setLoading(false)
let records = recordData.data.listRecordGames.items.map(
(item) => item.name
)
let result = Object.values(
records.reduce((acc, el) => {
if (!acc[el]) acc[el] = { name: el, plays: 0 }
acc[el].plays++
return acc
}, {})
)
updateRecords(result)
} catch (err) {
console.error(err)
}
}
return { records, loading }
}
export default useLoadRecords
I would make a hook for the data and setData to it when fetching ,
you can clone the data using spread operator and this pass it.
or better return that component only if there is something in data for example
{
data && <CustomPieChart data={recordGames} />
}
and it would be nice to make a some loader (gif/svg) using your loading hook so it can be shown when the data is still being fetched.

Getting a map undefined error for search engine results page

I keep getting this error when it kicks over to my Suggestion method.
Uncaught TypeError: Cannot read property 'map' of undefined
in Suggestions line 4
Can anyone help me figure out why, please?
I am a newb so forgive my ignorance.
import React from 'react';
const Suggestions = props => {
const options = props.results.map (r => (
<li key={r.id}>
{r.name}
</li>
));
return <ul className="ul-list">{options}</ul>;
};
export default Suggestions;
Here is the rest of the code for the search bar. I removed api info but it was in there.
import React, {Component} from 'react';
import axios from 'axios';
import Suggestions from '../SearchBbg/suggestions';
import '../../style/searchbar.scss';
const API_KEY = '';
const API_URL = '';
class Search extends Component {
state = {
query: '',
results: [],
};
getInfo = () => {
axios
.get (`${API_URL}?api_key=${API_KEY}&prefix=${this.state.query}&limit=7`)
.then (({data}) => {
this.setState ({
results: data.data,
});
});
};
handleInputChange = () => {
this.setState (
{
query: this.search.value,
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getInfo ();
}
} else if (!this.state.query) {
}
}
);
};
render () {
return (
<form>
<input
className="search-bar"
placeholder="Search for..."
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
<Suggestions results={this.state.results} />
</form>
);
}
}
export default Search;
I believe you already destructred in your axios call data property. That might be the reason why you got undefined for props.results.
So try the following:
getInfo = () => {
axios
.get(`${API_URL}?api_key=${API_KEY}&prefix=${this.state.query}&limit=7`)
.then(({data}) => { // here already destructured
this.setState ({
results: data // removed => .data,
});
});
};
Additionally also worth to check for null or undefined value like props.results && props.results.map() before using .map(), so you can use like just to be sure:
const Suggestions = props => {
const options = props.results && props.results.map (r => (
<li key={r.id}>
{r.name}
</li>
));
return <ul className="ul-list">{options}</ul>;
};
I hope this clarifies!
Your results array is undefined.
Check if you are passing results as a prop to Suggestions and if results array is not undefined.
Edit:
Seems that you are assigning some null or undefined to results, the only way this happens is on your api response.
Check if you are parsing server response correctly.

Uncaught (in promise) TypeError: Cannot read property 'fisierUrl' of undefined

I create a react app and i want to fetch data (images urls)from my API.
in Detali .js i wrote this code:
Detalii.js
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import url from "../url";
import Header from "../components/Header";
const Detalii = () => {
const [info, setInfo] = useState({});
const { id } = useParams();
useEffect(() => {
async function getData() {
try {
const res = await fetch(`${url}/detalii/${id}`);
const data = await res.json();
setInfo(data);
} catch (error) {
console.log(error);
}
}
getData();
}, [id]);
//console.log(info);
return (
<div className="bg-detalii">
<Header info={info} />
</div>
);
};
export default Detalii;
in Header conponent i display image like this:
Header.js:
import React from "react";
const Header = ({ info }) => {
console.log(info);
return (
<div className="header">
<img src={info.fisier.fisierUrl} className="img-detalii" alt="poza" />
<p className="text-detalii">{info.nume}</p>
</div>
);
};
export default Header;
in my console info.fisier object is like this:
in Detali.js it shows me the error when it displays image.how to fix it? thanks
look at the src={info.fisier.fisierUrl} especially at info.fisier.fisierUrl.
At the beginning your request is in progress and your info is equal to {}.
So it's {}.fisier.fisierUrl. {}.fisier is undefined. And undefined.fisierUrl throws an error
How to fix:
Instead of src={info.fisier.fisierUrl} try src={info && info.fisier && info.fisier.fisierUrl}
I believe the issue is that you try to render <Header /> before the fetch call has returned and setInfo has been called.
One solution could be to render Header only when info is "truthy". I.e.
return (
<div className="bg-detalii">
{info && (<Header info={info} />)}
</div>
);

Categories

Resources