Image source in Async function?

I am trying to render an image being called within an asynchronous function, but the value is not assigned based on API data response.

Code:

import React from 'react';

import { TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Icon2 from 'react-native-vector-icons/MaterialCommunityIcons';
import axios from 'axios';
import { Container, Left, Avatar, Info, Name, Time } from './styles';

export default function Appointments({ data }) {

  asd = async () => {
    const user = data.item.userLogin.toLowerCase();
    const response = await axios.get(
      `https://api4.successfactors.com/odata/v2/Photo?$filter=tolower(userId) eq '${user}'`,
      {
        auth: {
          username: 'Apiuser',
          password: 'Apipass',
        },
      }
    );
    const encodedData = response.data.d.results[0].photo.toString();
    console.log(encodedData);
    return encodedData;
  };

  return (
    <Container>
      <Left>
        <Avatar
          source={{
            uri: `data:image/png;base64,${this.asd()}`,
          }}
        />
        {console.log(this.asd())}
        <Info>
          <Name>
            {data.item.userOnDesk} {'n'}({data.item.userLogin})
          </Name>
          <Time>
            <Icon2 name="office-building" size={20} color="#000" />
            Prédio: {data.item.Predio}
          </Time>
          <Time>
            <Icon2 name="chair-school" size={20} color="#000" />
            Mesa: {data.item.Desk}
          </Time>
          <Time>{data.item.Andar} ºAndar</Time>
        </Info>
      </Left>
      <TouchableOpacity onPress={() => {}}>
        <Icon name="person" size={20} color="#005BAF" />
      </TouchableOpacity>
    </Container>
  );
}

How do I load the variable before executing a return? I need my method of rendering the image to be asynchronous, if I put the “Appointments” function asynchronous, it doesn’t work.

2 Answers

If you call this.asd() you will just render [object Promise] because asd is an async function, which returns a promise.

You need to load the photo in useEffect(() => { /* loado the photo */}, [])

export default function Appointments({ data }) {
  const [uri, setUri] = React.useState('')  

  React.useEffect(() => {
    const asd = async () => {
      const user = data.item.userLogin.toLowerCase();
      const response = await axios.get(
        `https://api4.successfactors.com/odata/v2/Photo?$filter=tolower(userId) eq '${user}'`,
        {
          auth: {
            username: 'Apiuser',
            password: 'Apipass',
          },
        }
      );
      const encodedData = response.data.d.results[0].photo.toString();
      console.log(encodedData);
      setUri(encodedData);
    };
    asd()
  }, [])

  return (
      ...
      <Avatar
        source={{
          uri: `data:image/png;base64,${uri}`,
        }}
      />
  )

}

Why it does not work

asd() is an async function. That means, it will return a promise, NOT the value (your base64 string). Read more about that here.

How it can be solved

Well, you could just do ${await this.asd()}. Because you are using await, you’d have to make render() async. But that is not possible in react. Besides, you’d be refetching the data on every component rerender.

A better approach would be to put the data in the state of your component:

// You can supply a default value as argument to useState()
const [avatarSrc, setAvatarSrc] = React.useState(""); 

// To retrieve the value:
<Avatar
  source={{
    uri: avatarSrc,
  }}
/>

Then you can fetch your data with the useEffect hook:

React.useEffect(() => {
  asd()
  .then((data) => setAvatarSrc(`data:image/png;base64,${data}`))
}, []);

Note the empty array as second parameter. This prevents from refetching on every new render, see https://css-tricks.com/run-useeffect-only-once/.

Archive from: https://stackoverflow.com/questions/59070347/image-source-in-async-function

Leave a Reply

Your email address will not be published. Required fields are marked *