Search code examples
javascriptreactjsasynchronouspromiseasync-await

How to async await in react render function?


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?


Solution

  • Edit 6/3/2024

    React Server Components are now available when using e.g. NextJS, and the correct code would look something like this:

    import Geocode from "react-geocode";
    import _ from 'lodash-es';
    
    const getAddressData = async (getCompanyUserRidesData=[]) => 
      Promise.all(getCompanyUserRidesData.map(async (userRides) => {
        const addr = await Geocode.fromLatLng(22.685131,75.873468);
        const address = addr.results[0].formatted_address;
        const email = _.get(userRides,'driverId.email', '');
        const mobile = _.get(userRides,'driverId.mobile', '');
        return { address, email, mobile };
      }));
    
    async function GeoServerComponent({ phase, getCompanyUserRidesData}) {
      const data = await getAddressData(getCompanyUserRidesData);
      return (
        <table>
          <tbody>
          {data.map(({ address, email, mobile }, index) => {
            return (
              <tr key={index}>
                <td>
                {address}
                </td>
                <td>Goa</td>
                <td>asdsad</td>
                <td>{email}</td>
                <td>{mobile}</td>
              </tr>
            );
          })}
          </tbody>
        </table>
      );
    }
    

    Original Answer:

    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} />
    }