I'm fetching data from an ExpressJS API.
The data returns and Object containing two Objects with the keys key1 and key2. These Objects contain Arrays of Objects as their values. If I print out the Arrays inside the HTML by doing {JSON.stringify(data["key1"])} I get all the data displayed as a stringified Array. When I'm trying to iterate over the data instead, it shows me a TypeError:
Uncaught TypeError: data.key1 is undefined
import React, { useEffect, useState } from "react";
export default function Home() {
const [data, setData] = useState([]);
const fetchData = () => {
return fetch("http://localhost:1337/").then((response) => response.json()).then((data) => setData(data));
}
useEffect(() => {
fetchData();
}, []);
return (
<div class="h-screen">
<div class="container mx-auto py-10 h-full text-white">
{JSON.stringify(data["key1"])}
{JSON.stringify(data["key2"])}
{data["key1"].forEach(function(item, index) {
return <div>{item}</div>
})}
</div>
</div>
);
}
const [data, setData] = useState([]);
Here you set the initial value of data as [] so no key1, key2 keys are not defined.
Initially, when your component renders
value of data is [] so you get an undefined error for key1 and key2.
You can solve this problem by adding check on data
{data.length > 0 &&
<div class="container mx-auto py-10 h-full text-white">
{JSON.stringify(data["key1"])}
{JSON.stringify(data["key2"])}
{data["key1"].forEach(function(item, index)
return <div>{item}</div>
})}
</div>
}
This will solve your problem.
Related
I am having a problem while getting data from supabase .
Could any one help me
`
import Link from "next/link";
import { supabase } from "../../supabase"
async function Index(){
const { data, error} = await supabase.from("Employees").select("*")
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
`
I tried using map, other function, and looked it up yet nothing works
The problem is how you are fetching the data. Try fetching your data inside an useEffect hook:
import Link from "next/link";
import { supabase } from "../../supabase";
import { useState, useEffect } from "react";
function Index() {
// const { data, error } = await supabase.from("Employees").select("*")
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
let cancelFetch = false; // to avoid race conditions on React18
supabase
.from("Employees")
.select("*")
.then((res) => {
if (!cancelFetch) {
setData(res.data);
setError(res.error);
}
});
return () => {
cancelFetch = true;
};
}, []);
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return <h1>{index.name}</h1>;
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
);
}
export default Index;
More info on fetching and useEffect here: https://beta.reactjs.org/apis/react/useEffect#fetching-data-with-effects
Your source code is invalid. React components should always be a function (or class) that returns a react object. it does not accept a promise that returns a react object.
You will probably want to use react's useEffect to solve this problem:
import { useState, useEffect } from "react";
import Link from "next/link";
import { supabase } from "../../supabase"
async function Index(){
const [data, setData] = useState()
const [error, setError] = useState()
useEffect(() => {
supabase.from("Employees").select("*")
.then(data => setData(data))
.catch(err => setError(err))
}, [])
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
Your component cannot be async, because it returns a Promise and React doesn't like that.
There is a cool function on Next.js that allows you to fetch data asynchronously, try that:
function Index({ data }) {
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
export async function getServerSideProps() {
const { data, error} = await supabase.from("Employees").select("*")
return {
props: {
data: data
}
}
}
More here: https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
Based on the way you are fetching data, I believe you are using next13 and you are in app directory. When you rendered jsx
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
index refers to each element inside the data array. Most likely index.name is an object. that is why it is throwing that error.
console.log("index name",index.name)
If you are using async functional component, you should be using Suspense api. Create a separeate component maybe async Users, fetch the data inside this component, and when you want to display the users inside the Index
import {Suspense} from "react"
function Index(){
return (
<>
....
<Suspense fallback={ <h1>Users are loading...</h1>} >
<Users/>
</Suspense>
....
</>
)
}
You only use async component inside app folder and server component.
I am waiting for the data (when the user submit) i fetch the data then return the Temperature with useState() but i wanted to return a header with it like Temperature:12°C.
Something else is that i wanna round the temperature to 2 decimal places but i don't know how to do so
here is my code:
import axios from 'axios';
import React from 'react'
import {useState, useEffect} from 'react'
import './style.css'
import rain from './use1.png'
function App() {
const [name,setName] = useState('Cairo')
const [res, setRes] = useState(null)
const [pic, setPic] = useState(null)
const [temp, setTemp] = useState('')
const getApi = e => {
axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${name}&appid=my_key`)
.then(response=> {
console.log(response.data);
setRes(response.data.name)
setTemp(response.data.main.feels_like-273.15+'°C');
Math.round(temp)
setPic(`https://openweathermap.org/img/w/${response.data.weather[0].icon}.png`)
})}
const handleChange = e => {
setName(e.target.value);
};
return (
<div>
<img className="wallpaper" src={rain}></img>
<div className="content">
<input placeholder="Search.." className="input" onChange={handleChange}></input>
</i>
</div>
<div className="content2">
<h1 className="name">{res}</h1>
<img src={pic}></img>
<h1 className="temp">{temp}</h1>
</div>
</div>
);
}
export default App;
Add a useEffect hook so your component re-renders after your temp state changes.
useEffect(() => {}, [temp]);
In order to round to two decimal places...well, usually I don't like telling people this but that's an extremely easy thing to find out from just using Google or another search engine.
JavaScript math, round to two decimal places
You could do it like:
`Temperature ${(response.data.main.feels_like-273.15).toFixed(2)} °C`
You can do like this
const [temp, setTemp] = useState('')
setTemp(response.data.main.feels_like-273.15+'°C');
return (
<div>
<h1 className="temp"> Temperature {temp?.toFix(2)}</h1>
</div>
)
Could you please provide the full component's code, or at least where is response coming from? Anyway, If the response is being passed from a parent component, then I see no reason to use useState at all.
But if you are fetching the data in the same component, then you need to fetch the data asynchronously:
function component(){
const [temp, setTemp] = useState('Loading temperature...')
useEffect(() => {
fetchTemp().then((response) => {
setTemp(response.data.main.feels_like-273.15+'°C');
})
}, [])
return (
<div>
<h1 className="temp">{temp}</h1>
</div>
)
}
About rounding the number, you can use toFixed(decimals) method.
Example:
let number = 900.1234
console.log(number.toFixed(2))
I have the following JSON array :-
[
{"A":"Player","B":"Position","C":"Age","D":"Acc","E":"Wor","F":"Vis","G":"Tec","H":"Tea","I":"Tck","J":"Str","K":"Sta","L":"Pos","M":"Pen","N":"Pas","O":"Pac","P":"OtB","Q":"Nat","R":"Mar","S":"L Th","T":"Lon","U":"Ldr","V":"Jum","W":"Hea","X":"Fre","Y":"Fla","Z":"Fir","AA":"Fin","AB":"Dri","AC":"Det","AD":"Dec","AE":"Cro","AF":"Cor","AG":"Cnt","AH":"Cmp","AI":"Bra","AJ":"Bal","AK":"Ant","AL":"Agi","AM":"Agg"},
{"A":"Cyril Ruffier_YP22I ","B":"AM (R), ST (C)","C":"23","D":"14","E":"13","F":"12","G":"11","H":"14","I":"4","J":"13","K":"13","L":"8","M":"8","N":"11","O":"13","P":"15","Q":"8","R":"2","S":"4","T":"8","U":"12","V":"13","W":"8","X":"7","Y":"11","Z":"16","AA":"9","AB":"10","AC":"17","AD":"12","AE":"9","AF":"5","AG":"8","AH":"13","AI":"4","AJ":"18","AK":"10","AL":"11","AM":"7"},
{"A":"John Latouchent_YP26B ","B":"M (L), AM (RLC), ST (C)","C":"19","D":"15","E":"14","F":"10","G":"13","H":"14","I":"4","J":"10","K":"13","L":"12","M":"4","N":"13","O":"13","P":"12","Q":"11","R":"9","S":"4","T":"7","U":"10","V":"9","W":"9","X":"9","Y":"14","Z":"9","AA":"8","AB":"11","AC":"14","AD":"9","AE":"10","AF":"8","AG":"9","AH":"10","AI":"9","AJ":"17","AK":"11","AL":"14","AM":"12"}
]
and I am trying to map it so that I can display it in this component:-
import React from 'react';
import { getStickyState } from './Utils';
const PlayersDisplay = () => {
const [players, setPlayers] = getStickyState([], 'players');
return (
<>
{players && (
<div className='attributesDisplay-container'>
<div className='container'>
{console.log(players)};
{players.map((player) => {
return <div>{player}</div>;
})}
</div>
</div>
)}
</>
);
};
export default PlayersDisplay;
This is my getStickyState function in my Utils component:-
export function getStickyState(defaultValue, key) {
const [value, setValue] = useState(defaultValue);
React.useEffect(() => {
const stickyValue = window.localStorage.getItem(key);
if (stickyValue !== null) {
setValue(JSON.parse(stickyValue));
}
}, [key]);
return [value, setValue];
}
and this is the setStickyState :-
export function setStickyState(defaultValue, key) {
const [value, setValue] = useState(defaultValue);
React.useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
The console.log is outputting the JSON array I posted here.
However players.map is throwing the error .map is not a function. I also tried JSON.parse(players) but to no avail.
Any help will be very much appreciated.
Thanks
players is a string, not an array, seeing as console.log(players[0]) displays '['. I don't know why, from the code, since you call JSON.parse. Perhaps you have saved the players array incorrectly to localstorage, for example by using JSON.stringify twice. We don't have the code you used to store the data to localstorage.
I am trying to fetch data from https://randomuser.me/api/ using react useEffect hook. Instead of the data to be displayed, I get an error, "Error: Objects are not valid as a React child (found: object with keys {number, name}). If you meant to render a collection of children, use an array instead". I logged the results on my console, I realised the problem is from some of the nested objects, which outputs [object Object]. However, I don't know why some other nested objects display correctly. Below is my code:
import { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import '../../App.css';
const UsersList = () => {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
useEffect(() => {
fetch("https://randomuser.me/api/")
.then(res => res.json())
.then(
(result) => {
setIsLoaded(true);
setItems(result.results);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, [])
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
{items.map((item, id) => (
<div className="card content-panel" key={id}>
<img src={item.picture.medium} alt="Profile pic" className="avatar-user-list" />
<div className="card-text">
<p>
{item.name.first} {item.name.last}
</p>
<p>{item.location.street}</p>
<p>{item.email}</p>
</div>
<div className="arrow-bg">
<span className="arrow">→</span>
</div>
</div>
))}
</div>
);
}
}
export default UsersList;
item.location.street is an object.
According to the response API, this is the street object.
"street": {
"number": 4850,
"name": "Rue Dumenge"
},
Instead of rendering
<p>{item.location.street}</p>
You should render something like
<p>{`${item.location?.street?.number} ${item.location?.street?.name}`}</p>
I want to filter data and implement in search bar. In Hook/index.js component I am fetching and filtering data inside useEffects. Then I am passing props in App.js. Afterwards I have a Searchbar component, where I am listening to the input and here it must work. I get undefined.
Hook/index.js component
import React, { useState, useEffect } from "react";
import "./hook.scss";
export default () => {
const [data, setData] = useState([]);
const [error, setError] = useState(null);
const [search, setSearch] = useState("");
const fetchData = () => {
fetch("https://restcountries.eu/rest/v2/all")
.then((res) => res.json())
.then((result) => setData(result))
.catch((err) => console.log("error"));
};
useEffect(() => {
const searchResult =
data && data.filter((item) => item.name.toLowerCase().includes(search));
setSearch(searchResult);
}, []);
useEffect(() => {
fetchData();
}, []);
return [data, error];
};
App.js
import React, { useState }from "react";
import Header from "./components/Header";
import SearchBar from "./components/SearchBar";
import Flag from "./components/Flag";
import useCountries from "./Hooks";
import CountryList from "./components/CountryList";
import "./App.scss";
export default function App() {
const [data, error] = useCountries();
return (
<div className="App">
<SearchBar /> // {/*this throws an error <SearchBar data={data}/> */}
<Header />
{data &&
data.map((country) => (
<div className="CountryList" key={country.name}>
<Flag flag={country.flag} />
<CountryList
population={country.population}
name={country.name}
region={country.region}
/>
{country.languages.map((language, languageIndex) => (
<CountryList key={languageIndex} language={language.name} />
))}
</div>
))}
<useCountries />
</div>
);
return [data, error]
}
Searchbar component
import React, {useState} from "react";
import "./SearchBar.scss";
export default function SearchBar({data}) {
const [search, setSearch] = useState("");
function handleChange(e) {
setSearch(e.target.value);
}
return (
<div className="SearchBar">
<input
className="input"
type="text"
placeholder="search country ..."
value={data}
onChange={handleChange}
/>
{data && data.filter((item) => item.name.toLowerCase().includes(search))}
</div>
);
};
You are sending data variable to input instead of search variable.
In JS filter return array and DOM cannot display array since it is not html or jsx so you need to convert array to jsx with map. with map you can return array or jsx
<div className="SearchBar">
<input
className="input"
type="text"
placeholder="search country ..."
value={search} // change here
onChange={handleChange}
/>
<ul>{(data || []).filter((item) => item.name.toLowerCase().includes(search)).map(e=>(<li key={e.name}>{e.name}</li>))}</ul> /change here
</div>
Your new .filter() Array contains Objects inside it! You need to .map() it before return as Objects are not valid as a React child.
{ data?.filter((item) => item.name.toLowerCase().includes(search)).map((element =>
<>
/* Your code goes here! */
</>) }
Explanation:
Array.prototype.filter() returns a new Array and in your case your Array is filled with Objects, like this:
{data && data.filter((item) => item.name.toLowerCase().includes(search))}
// The code above returns an Array just like below.
const array = [ {name: 'Brazil' /*...others properties*/}, {name: 'USA' /*...others properties*/}, {name: 'England' /*...others properties*/} ];
When you return array, React reject to mount your Objects, cus it can't understand what to do. That's why you map it, to have access to each Object inside it.