How to async await in react render function?
Solution 1
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} />
}
Solution 2
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.
Profer
Updated on December 04, 2021Comments
-
Profer over 2 years
I am pretty much familiar with the
async await
but with back endnodejs
. 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 usingreact-geocode
I can get the place name for a singlelat lng
but I want to use that inside the map function to get the places names. SO as we know itasync
call I have to useasync 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?