skip to Main Content

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.

2

Answers


  1. 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.

    Login or Signup to reply.
  2. The error you’re encountering is because you’re trying to return a Promise ([object Promise]) from within the map function, but React expects synchronous values to be returned in order to render JSX.

    In your code, you’re using async within the map callback function, which means that some of your return values might be Promises. React doesn’t know how to handle Promises directly as JSX content. To fix this, you need to structure your code so that you’re only returning synchronous JSX elements.

    Here’s how you can achieve this:

    // Assuming this is inside a React component's render or JSX-returning function
    
    // Use Promise.all to await all the necessary async operations
    const mappedJSX = await Promise.all(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 systemJSX; // Replace with actual JSX for system messages
      }
    
      if (...) {
        // My messages
        return myJSX; // Replace with actual JSX for your messages
      }
    
      if (...) {
        // Other person's messages
        return otherPersonJSX; // Replace with actual JSX for other person's messages
      }
    
      return null; // Default case, or you can handle this differently
    }));
    
    // Now you can render the mapped JSX
    return <div>{mappedJSX}</div>;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search