Home > Software design >  Asynchronously map JSX in React
Asynchronously map JSX in React

Time:08-07

I have async function that basically fetches a list from the backend, and I would create a html list on the FE with those elements. For that I want to do a map on that list. The problem is that since it's the result of a async function, it seems that I need add some details.

Based on my searches, I should do something similar to this:

const listItems = await Promises.all( podcastList.map(
      (podcast) =>{
      <ListItem value = {podcast} />}
      ));

However, I get the error:

Compiled with problems:X

ERROR in ./src/App.js

Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: Unexpected reserved word 'await'. (21:20)

The remaining functions are:

function ListItem(props) {
  return <li>{props.value}</li>;
}

async function podcast_list() {
  let podcast_list;
  const headers = { 'Content-Type': 'application/json' };
  const res = await fetch('/list_podcasts', { method: 'GET',headers});
  podcast_list = await res.json();
  console.log(podcast_list);
  return podcast_list
}
podcastList = podcast_list();

CodePudding user response:

JSX can't be returned from a component asynchronously. Separate your raw data from your JSX--await the request for the raw data, set state to trigger a rerender once the data arrives, then build the JSX UI.

const {useEffect, useState} = React;

// Mock for demonstration
fetch = () => Promise.resolve({
  json: () => Promise.resolve([
    "some podcast",
    "some other podcast",
  ])
});
//

const ListItem = ({value}) => (
  <li key={value}>{value}</li>
);

const Podcasts = () => {
  const [podcasts, setPodcasts] = useState([]);

  useEffect(() => {
    fetch("/list_podcasts")
      .then(res => res.json())
      .then(data => {
        setPodcasts(data);
      });
  }, []);

  return (
    <div>
      {podcasts.length ? (
          <ul>
            {podcasts.map(e => <ListItem value={e} />)}
          </ul>
        ) : <div>Loading...</div>
      }
    </div>
  );
};

ReactDOM.render(<Podcasts />, document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Use camelCase in JavaScript, not snake_case and don't forget to add a unique key attribute to list items.

CodePudding user response:

You don’t need to await on maps when rendering JSX elements. Just:

const listItems = podcastList.map((podcast) => <ListItem value={podcast} />)
  • Related