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
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 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 need to create a functional react component that automatically update the inner data every time there is a particular event.
I written the code below, but every time the callback is called the component is re-rendered and the variable myArray is set to empty array instead to be filled with all content coming in the callback.
What am I doing wrong? What's the approach I should use?
// event emulation
function onEventEmulator(callback, interval = 1000) {
setInterval(() => callback('content'), interval);
}
function PollingComponent(props) {
const [myArray, setMyArray] = useState([]);
useEffect(() => {
onEventEmulator(content => {
setMyArray([...myArray, content]);
});
}, []); // with or without passing the empty array as second param, result does not change.
return <ul>
<li>Callback</li>
<li>{myArray.length}</li>
</ul>;
}
Let's try this:
function onEventEmulator(callback, interval = 1000) {
setInterval(() => callback("content"), interval);
}
And then:
function PollingComponent(props) {
const [myArray, setMyArray] = useState([]);
useEffect(() => {
onEventEmulator(content => {
setMyArray(arr => [...arr, ...content]);
});
}, []);
return (
<ul>
<li>Callback</li>
<li>{myArray.length}</li>
</ul>
);
}
One thing that is missing here is a way to clear setInterval, but I guess that is not your concern here.
I am pretty much familiar with the async await but with back end nodejs. But there is a scenario came across to me where I have to use it on front end.
I am getting array of objects and in that objects I am getting lat lng of the places. Now using react-geocode I can get the place name for a single lat lng but I want to use that inside the map function to get the places names. SO as we know it async call I have to use async await over there.
Here is the code
import Geocode from "react-geocode";
render = async() => {
const {
phase,
getCompanyUserRidesData
} = this.props
return (
<div>
<tbody>
await Promise.all(_.get(this.props, 'getCompanyUserRidesData', []).map(async(userRides,index) => {
const address = await Geocode.fromLatLng(22.685131,75.873468)
console.log(address.results[0].formatted_address)
return (
<tr key={index}>
<td>
{address.results[0].formatted_address}
</td>
<td>Goa</td>
<td>asdsad</td>
<td>{_.get(userRides,'driverId.email', '')}</td>
<td>{_.get(userRides,'driverId.mobile', '')}</td>
</tr>
)
}))
</tbody>
</div>
)
}
But when I use async with the map function here it doesn't return anything. Can anyone please help me where I going wrong?
You should always separate concerns like fetching data from concerns like displaying it. Here there's a parent component that fetches the data via AJAX and then conditionally renders a pure functional child component when the data comes in.
class ParentThatFetches extends React.Component {
constructor () {
this.state = {};
}
componentDidMount () {
fetch('/some/async/data')
.then(resp => resp.json())
.then(data => this.setState({data}));
}
render () {
{this.state.data && (
<Child data={this.state.data} />
)}
}
}
const Child = ({data}) => (
<tr>
{data.map((x, i) => (<td key={i}>{x}</td>))}
</tr>
);
I didn't actually run it so their may be some minor errors, and if your data records have unique ids you should use those for the key attribute instead of the array index, but you get the jist.
UPDATE
Same thing but simpler and shorter using hooks:
const ParentThatFetches = () => {
const [data, updateData] = useState();
useEffect(() => {
const getData = async () => {
const resp = await fetch('some/url');
const json = await resp.json()
updateData(json);
}
getData();
}, []);
return data && <Child data={data} />
}
With the wrapper function below, delayed_render(), you can write asynchronous code inside a React component function:
function delayed_render(async_fun, deps=[]) {
const [output, setOutput] = useState()
useEffect(async () => setOutput(await async_fun()), deps)
return (output === undefined) ? null : output
}
This wrapper performs delayed rendering: it returns null on initial rendering attempt (to skip rendering of this particular component), then asynchronously calculates (useEffect()) the proper rendering output through a given async_fun() and invokes re-rendering to inject the final result to the DOM. The use of this wrapper is as simple as:
function Component(props) {
return delayed_render(async () => { /* any rendering code with awaits... */ })
}
For example:
function Component(props) {
return delayed_render(async () => {
const resp = await fetch(props.targetURL) // await here is OK!
const json = await resp.json()
return <Child data={json} />
})
}
UPDATE: added the deps argument. If your async_fun depends on props or state variables, all of them must be listed in deps to allow re-rendering. Note that passing deps=null (always re-render) is not an option here, because the output is a state variable, too, and would be implicitly included in dependencies, which would cause infinite re-rendering after the async_fun call completes.
This solution was inspired by, and is a generalization of, the Jared Smith's one.