javascriptreactjsnext.jsasync-awaitarray-map

Array.map started to error after adding an asynchronous call inside


I have a React component that maps through an array of messages and returns a bunch of JSX based on a few conditions:

messages.map(async (msg) => {
  let previewImage: URL | undefined = undefined;

  if (msg.mediaId) {
    previewImage = await storage.getFilePreview(
      Constants.BUCKET_ID,
      msg.mediaId
    );
  }

  if (...) {
    // System messages
    return JSX;
  }

  if (...) {
    // My messages
    return JSX;
  }

  if (...) {
    // Other person's messages
    return JSX;
  }
});

This used to work fine before I added the async function to get the preview image if the message contains a mediaId. However, after making the function async because I needed to await the getFilePreview call, I'm now getting this error:

Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.


Solution

  • First take into account the comment left by @ray as it explains everything. With this in mind, I recommend you create a component that will take care of rendering a message, so you can leave the responsibility of loading the preview of your image to it without affecting your messages.map(...) and your code will be more readable.

    For example, look at this snippet:

    function Message({ message }) {
      const [previewImage, setPreviewImage] = useState(undefined);
    
      useEffect(() => {
          let ignore = false;
    
          setPreviewImage(undefined);
          storage
            .getFilePreview(Constants.BUCKET_ID, msg.mediaId)
            .then((preview) => {
                if(!ignore) {
                  setPreviewImage(preview)
                }
              });
    
          return () => {
            ignore = true;
          }
        },
        [message.mediaId]
      );
    
      if (...) {
        // System messages
        return JSX;
      }
    
      if (...) {
        // My messages
        return JSX;
      }
    
      if (...) {
        // Other person's messages
        return JSX;
      }
    }
    

    Then your initial code would look like this:

    messages.map((msg) => <Message message={msg} key={msg.id} />);
    

    You can learn more about why I suggested getting the preview asynchronously using useEfffect here.