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:
Related
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>
}
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>)
Let's took a look a this functional component :
note : this is just an exemple
function Foo() {
const [data, setData] = useState([]);
const GetData = () => {
//setting the data...
setData([1, 2, 3]);
}
const ShowData = (data) => {
if(data)
console.log(data);
}
useEffect( () => {
GetData();
ShowData(data)
},[]);
console.log(data) // Here I get the new data normally;
return (
<>
<h2>Hello world ! </h2>
</>
)}
So my question is how can I get the updated value ( the new value of data ) to use it inside ShowData function ?
The best way is to use another useEffect with data as dependency, so everytime data is updated you will run showData() like the following
useEffect( () => {
GetData();
},[]);
useEffect( () => {
ShowData()
},[data]); // runs when data is updated
this way you don't need to pass data argument to showData fn. You will get the state updated.
On first render use
useEffect(() => {
GetData();
},[]);
Then we also add a useEffect with data in the dependency array - which means it will run the code every time data changes.
useEffect(() => {
ShowData(data)
},[data]);
Since data will be empty on first render you could do something like this in getData to just skip the function if data is not available yet.
const ShowData = (data) => {
if(!data) return; //if there is no data yet, dont read code below
console.log(data);
}
Jsfiddle demo
Currently working on a stock project for my portfolio and I am using finnhub as the API.
I can log everything to my console. However I cannot render it as the "data" is not globally declared and must be inside of a certain function.
I tried rendering globally but had no luck...
So my question is how do I make 'data' global so that I can render it inside of the "StockHeader's" return ?
Heres what I have so far...
import React,{ useState, useEffect } from 'react';
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
const getStocks = () => {
//setting stocks
setStocks(stocks)
}
//calling it once
useEffect(()=> {
getStocks();
}, [])
//using finhubs ready made code from documentation
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
//I can log the data but I cant show it in my component
console.log(data.c)
});
return (
<>
{/* This says that data is not defined */}
<h1>{data.c}</h1>
</>
)
}
export default StockHeader
You just need a little bit of code reorganization so that the API request only happens once and so that you can use setStocks to store it:
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
useEffect(()=> {
//this could be separated into a `getStocks` function if you want
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
console.log(data.c);
setStocks(data.c);
});
}, []);
return (
<>
{/* You probably don't want to render `stocks` itself, but this shows you how to get access to the variable */}
<h1>{stocks}</h1>
</>
)
}
I have a react component using Hooks where I click a button to make API calls to the Hacker News API and push the results into an array. Then I set the state of "stories" to be that array full of stories.
I have a second function fired off by a button which console logs the state of "stories" and console.log's a .map returning each stories title. All of this works just fine.
If I attempt to use a .map in the return of the component it does not work. If I initalize the state of "stories" to be ["test", "test1", "test2"] the .map works but as soon as I hit my button to set the state to be the array of stories the .map stops working. No error messages just the content goes away.
Here is where I import React and set the initial state, I've used Axios, Fetch and Wretch to make the API calls, all with the same results:
import React, { useState, useEffect } from 'react';
const axios = require('axios');
import wretch from "wretch"
function App () {
const [stories, setStories] = useState(["test", "test2", "test3"]);
Here is the function that I fire off to hit the API andf set the state:
function call () {
let storiesArr = [];
fetch('http://hacker-news.firebaseio.com/v0/topstories.json')
.then ((res) => res.json())
.then ((data) => {
for (let i = 0; i < 20; i++) {
fetch(`http://hacker-news.firebaseio.com/v0/item/${data[i]}.json`)
.then((res) => res.json())
.then((eachStory) => {
storiesArr.push(eachStory);
})
}
})
Here is a second function that I'm using to check that state was set with what I think it was and to make sure that a .map works on the state of "stories". This does work for me:
function test () {
console.log(stories);
stories.map((each) => {
return <p>{each.title}</p>
})
}
This is my return, the .map here does work with the initial state but does not once I set state to be the new array:
return (
<>
<h1 onClick={ () => call() } className="click">Fire off API calls</h1>
<h1 onClick={ () => test() } className="click">Test state of stories/<br/>.map each title</h1>
<table className="table">
<thead>
<tr>
<td>Table</td>
</tr>
</thead>
<tbody>
{
stories.map((each, i) => {
return <tr key={i}>
<td>{each.title ? each.title : each}</td>
</tr>
})
}
</tbody>
</table>
</>
);
I am unable to figure out why the .map works at first, no longer works in the return, but does work in a function....
I would greatly appreciate any help any one can be.
Your data fetching look a little messy, you know you can use Promise.all instead of pushing to an array and looping.
I have added a check to see if the component is still mounted before setting state.
const isMounted = useIsMounted();
//other code, I imagine useEfffect
function call() {
fetch(
'http://hacker-news.firebaseio.com/v0/topstories.json'
)
.then(res => res.json())
.then(data =>
Promise.all(
data.map(item =>
fetch(
`http://hacker-news.firebaseio.com/v0/item/${item}.json`
).then(res => res.json())
)
)
)
.then(
result => isMounted.current && setStories(result)
);
}
Also: http://hacker-news.firebaseio.com/v0/topstories.json returns over 400 items, that will cause you to make over 400 requests for each item, I don't think hacker-news will appreciate that so maybe slice the result and or page it.
I think this is more of an async processing issue than a setState one. Here is a handy all in one (simplified) example
import React, { useState } from "react";
import ReactDOM from "react-dom";
// gets list of article ids
const getStoryList = async () => {
const res = await fetch(
"https://hacker-news.firebaseio.com/v0/topstories.json"
);
return await res.json();
};
// iterates over article list and returns a promise.all
const getStories = (articles, quantity) => {
return Promise.all(
articles.slice(0, quantity).map(async article => {
const storyRes = await fetch(
`https://hacker-news.firebaseio.com/v0/item/${article}.json`
);
return await storyRes.json();
})
);
};
// maps your response data
const formatStories = stories =>
stories.map(({ by, id, url, title = "No Title" }) => ({
id,
title,
url,
by
}));
function App() {
const [stories, setStories] = useState([]);
const call = async () => {
// first get list of stories
const res = await getStoryList();
// then async request all of the individual articles
// and push them into a group of promises
const storiesArr = await getStories(res, 20);
// then set your state.
setStories(formatStories(storiesArr));
};
return (
<div className="App">
<button onClick={call} className="click">
Fire off API calls
</button>
<table className="table">
<thead>
<tr>
<td>Table</td>
</tr>
</thead>
<tbody>
{stories.map(story => {
return (
<tr key={story.id}>
<td>
<a href={story.url}>{story.title}</a> by {story.by}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);