I am working with Reactjs and using nextjs framework,Right now i am trying to fetch data (url is - https://dummyjson.com/products) using map function but i am getting following error
TypeError: Cannot read property 'length' of undefined
Here is my current code
import { useEffect, useState } from "react"
export default function Test() {
const [data, setData] = useState<any>();
useEffect(() => {
const callData = async () => {
const data = await fetch('https://dummyjson.com/products').then(data => data.json())
console.log(data);
setData(data)
}
callData()
}, [])
return(
<div>
{
data.length ? data.map(({}) => <p key={data.id}>{data.title}</p>) : <h3>There are no records yet</h3>
}
</div>)
}
Initially data is undefined, so use optional chaining to check nested properties.
The returned data is an object; you want to access the products field.
Name the first parameter to the Array#map callback so you can actually access it.
{data?.products?.length ? data.products.map(product => <p key={product.id}>{product.title}</p>)
: <h3>There are no records yet</h3>}
Try to initialize data state with an empty array:
const [data, setData] = useState<any>([]);
You should also change
data.map(({}) => <p key={data.id}>{data.title}</p>)
to
data?.map(({id,title}) => <p key={id}>{title}</p>)
//Follow this code. your data in the products array so if you want to map then you have to pass array.
import { useEffect, useState } from "react";
export default function Test() {
const [data, setData] = useState<any[]>([]);
useEffect(() => {
const callData = async () => {
const data = await fetch('https://dummyjson.com/products').then(data => data.json())
console.log(data);
setData(data?.products)
}
callData()
}, [])
return(
<div>
{
data.length ? data.map((product) => <p key={product.id}>{product.title}</p>) : <h3>There are no records yet</h3>
}
</div>)
}
The problem with your code is that you are missing the destructuring of elements in the map function. The correct way would be
{
data.length ? data.map(({title, id}) => <p key={id}>{title}</p>) :
<h3>There are no records yet</h3>
}
Related
I am working on Reactjs and using nextjs,Right now i am trying to fetch data using "map" function,How can i do this ? Here is my current code
import { useEffect, useState } from "react"
export default function Test() {
const [data, setData] = useState<any>();
useEffect(() => {
const callData = async () => {
const data = await fetch('https://dummyjson.com/products').then(data => data.json())
console.log(data);
setData(data)
}
callData()
}, [])
return (
//want to use map function here
);
}
Well dummyjson will return you an object wich will contain { products, total, skip, limit } in your return you can write
{data.products.map((product) => <p key={product.id}>{product.title}</p>)}
paragraph in map can be your ArticleItem or everything you want.
so you could do this, check if the state has any data in it then map through, if not show some other message.
Not sure how your data state is structured, but this should help
return(
<div>
{
data.length ? data.map(({id: number, title: string}) => <p key={id}>{title}</p>) : do something if data is empty
}
</div>)
I am trying to render listed property information from an array of objects. I used this method in another part of my project with success, but in this instance, I am not getting anything at all.
here is the code I have
import { database } from "../../components/firebase";
import { ref, child, get } from "firebase/database";
import { useState, useEffect } from "react";
export default function Dashboard() {
const dbRef = ref(database);
const [users, setUsers] = useState([]);
const array = [];
const getData = () => {
get(child(dbRef, "users/"))
.then((snapshot) => {
const data = snapshot.val();
setUsers(data);
})
.catch((err) => {
console.log(err);
});
};
const getProperties = () => {
Object.values(users).forEach((user) => {
Object.values(user?.properties).forEach((property) => {
array.push(property);
console.log(property);
});
});
console.log(array);
};
useEffect(() => {
getData();
getProperties();
}, [dbRef]);
return (
<>
<div>Properties </div>
<div>
{array.map((property) => (
<div key={property.property_id}>
<h1>{property?.property_name}</h1>
<p>{property?.description}</p>
<p>{property?.rooms}</p>
<p>{property?.phone}</p>
</div>
))}
</div>
<p>oi</p>
</>
);
}
Nothing happens, it only prints "properties" and "oi"
getData is asynchronous. When you execute getProperties, your users state will still be its initial, empty array value.
You don't appear to be using users for anything else but assuming you want to keep it, the easiest way to drive some piece of state (array) from another (users) is to use a memo hook.
// this is all better defined outside your component
const usersRef = ref(database, "users");
const getUsers = async () => (await get(usersRef)).val();
export default function Dashboard() {
const [users, setUsers] = useState({}); // initialise with the correct type
// Compute all `properties` based on `users`
const allProperties = useMemo(
() =>
Object.values(users).flatMap(({ properties }) =>
Object.values(properties)
),
[users]
);
// Load user data on component mount
useEffect(() => {
getUsers().then(setUsers);
}, []);
return (
<>
<div>Properties </div>
<div>
{allProperties.map((property) => (
<div key={property.property_id}>
<h1>{property.property_name}</h1>
<p>{property.description}</p>
<p>{property.rooms}</p>
<p>{property.phone}</p>
</div>
))}
</div>
<p>oi</p>
</>
);
}
The memo hook will recompute allProperties any time users is changed.
If you don't need the users state, then there's not much need for the memo hook. Instead, just maintain the state you do need
const [allProperties, setAllProperties] = useState([]); // init with empty array
useEffect(() => {
getUsers().then((users) => {
setAllProperties(
Object.values(users).flatMap(({ properties }) =>
Object.values(properties)
)
);
});
}, []);
I am trying to render a component within a component file that relies on data from an outside API. Basically, my return in my component uses a component that is awaiting data, but I get an error of dataRecords is undefined and thus cannot be mapped over.
Hopefully my code will explain this better:
// Component.js
export const History = () => {
const [dateRecords, setDateRecords] = useState(0)
const { data, loading } = useGetRecords() // A custom hook to get the data
useEffect(() => {
fetchData()
}, [loading, data])
const fetchData = async () => {
try {
let records = await data
setDateRecords(records)
} catch (err) {
console.error(err)
}
}
// Below: Render component to be used in the component return
const GameItem = ({ game }) => {
return <div>{game.name}</div>
}
// When I map over dateRecords, I get an error that it is undefined
const renderRecords = async (GameItem) => {
return await dateRecords.map((game, index) => (
<GameItem key={index} game={game} />
))
}
const GameTable = () => {
return <div>{renderRecords(<GameItem />)}</div>
}
return(
// Don't display anything until dateRecords is loaded
{dateRecords? (
// Only display <GameTable/> if the dateRecords is not empty
{dateRecords.length > 0 && <GameTable/>
)
)
}
If dateRecords is meant to be an array, initialize it to an array instead of a number:
const [dateRecords, setDateRecords] = useState([]);
In this case when the API operation is being performed, anything trying to iterate over dateRecords will simply iterate over an empty array, displaying nothing.
You've set the initial state of dateRecords to 0 which is a number and is not iterable. You should set the initial state to an empty array:
const [dateRecords, setDateRecords] = useState([]);
I am trying to hit an api(sample response) which gives list of objects and render it in a table in react. Following is my code. I am getting error as data is not defined
Here is the code when I hardcoded data, the table got rendered perfectly
import React, { useEffect, useState } from "react";
function renderTableData() {
return data.map((student, index) => {
const { config, text, source_link, tab_type } = student //destructuring
return (
<tr key={config}>
<td>{config}</td>
<td>{text}</td>
<td>{source_link}</td>
<td>{tab_type}</td>
</tr>
)
})
}
const useFetch = (url) => {
const [data, setData] = useState('');
const [loading, setLoading] = useState(true);
useEffect(async () => {
const response = await fetch(url, {
method: 'GET',
})
const data = await response.json()
console.log(data)
setData(data)
setLoading(false)
}, []);
return { data, loading };
}
export default function CreateTestcaseStep2() {
const { data, loading } = useFetch(ENPOINT_URL_GOES_HERE)
return (
<div>
<h1 id='title'>React Dynamic Table</h1>
<table id='students'>
<tbody>
{renderTableData()}
</tbody>
</table>
</div>
);
}
Please suggest where I am doing wrong as renderTableData function is not able get the data object.
renderTableData is defined outside your functional component and you refer to data variable within it, I suppose it doesn't know which data variable to refer to? I am surprised you didn't get an error about this.
Try passing the data variable as parameter to the function.
I'm refactor you example.
Do not use useEffect asyc.
useEffect(() => {
axios.get(url).then((response) => {
setData(response);
setLoading(false);
});
}, [url]);
If you want to make table body with a function, need to pass data using parameters.
const renderTableData = (data) => {
// ^^^^ Pass data using function parameters,
// this function it's possible to use in another table with the same data
return data?.map((student) => {
const { id, name, username, email } = student;
return (
<tr key={id}>
<td>{name}</td>
<td>{username}</td>
<td>{email}</td>
</tr>
);
});
};
To render table body with function pass state
<tbody>{renderTableData(data)}</tbody>
// ^^^^ Passing state to render table body
See live demo, pass data into function using params:
I am trying to update my 'state' array and insert items of type String into it with 'setState' but it doesn't works.
I know it's not work with push().
I also tried to update my 'state' array with the spread operator but it also doesn't work.
Here my code:
import React, { useState } from 'react';
import _, { debounce } from 'lodash';
export default function Search() {
const [state, setState] = useState([])
const handleChange = debounce(async (value) => {
const url = `http://localhost:3100/`
if (value == '') {
return
}
let response = await fetch(url, {
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify({ value })
})
let test = await response.json()
console.log(test)
setState(state.concat(test))
// setState([...state, test]) it also doesn't work
console.log(state)
}, 1000)
return (
<>
<div>
<input onChange={e => handleChange(e.target.value)} />
</div>
</>
)
}
The 'state' array remains empty. I need to understand why please.
1.) Change if(value == '') to if(value ==='')
2.) console.log(state) after your setState will return the previous value of state as the component has not refreshed yet. Look at the example here: https://codesandbox.io/s/friendly-ives-vvo13?file=/src/App.js:103-474 and type something and look at the console. Then type something else and look at the console. You will see the console is showing the state of what you previous typed. However, if you look at the {state} rendered inside of the return, it will show you the current state.
export default function App() {
const [state, setState] = useState([]);
const handleChange = debounce(async value => {
let test = ["cars", "boat", "bike"];
setState([...test, value]);
console.log(state);
}, 1000);
return (
<>
<div>
{state}
<input onChange={e => handleChange(e.target.value)} />
</div>
</>
);
}
So you are setting state, just accessing/reading it in the wrong place.
https://codesandbox.io/s/next-js-infinite-scroll-3vfem?file=/pages/Content.js
I added one line to this function from the demo above
const getMorePost = async () => {
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos?_start=${posts.length}&_limit=20`
);
const newPosts = await res.json()
setHasMore(!!newPosts.length)
setPosts((post) => [...post, ...newPosts])
}
now scroll completes ok.