2

I am creating an app using expo. You can check the snack here
I am also giving the code here:

    import React, {Component} from 'react';
import { ActivityIndicator, Text, View, StyleSheet, FlatList, Alert, TouchableOpacity } from 'react-native';
import {Avatar, Card, Button, Divider, ListItem, Image} from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import Constants from 'expo-constants';
import HTML from 'react-native-render-html';
import UserAvatar from 'react-native-user-avatar';
import { StackNavigator } from 'react-navigation';
import { createAppContainer} from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';

class HomeScreen extends React.Component{
  static navigationOptions =
   {
      title: '',

   };
  constructor(props){
    super(props);
    this.state = {
      Loading : true,
      data : []

    }
  }
  fetchLeash(){
     fetch('https://lishup.com/app/')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({ data: responseJson, Loading:false });
      }).catch((error) => {
        Alert.alert('error!');
      });
  }
fetchImage(getimg){
  fetch('https://lishup.com/app/fetch-image.php', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
   image: getimg
  })
}).then((response) => response.json())
      .then((responseJson) => {
      return (<Text>responseJson.url</Text>);
      }).catch((error) => {
        Alert.alert('error');
      });
}

  componentDidMount(){
    this.fetchLeash();
}

renderLeash = ({ item }) => (
    <View>
      <Card style={{ height:100, justifyContent: 'center', alignItems: 'center' }}>
      <ListItem
  leftAvatar={{
    title: item.user,
    source: { uri: item.userpic },
  }}
  title={item.user}
   subtitle={item.time} 
  chevron
/>  

      <Divider style={{margin:5, backgroundColor:'white'}} />
        <HTML html={item.text} />
        {this.fetchImage(item.images)}

      </Card>
    </View>
  )
   render(){
     if(this.state.Loading == true){
        return(
          <ActivityIndicator size="large" style={{marginTop:100}} color="#0000ff" />
        );
       }else{
         return(
       <View>
        <FlatList style={{width:400}}
        data={this.state.data}
        renderItem={this.renderLeash} />
       </View>
     );
       }

   }

}

const styles = StyleSheet.create({

});

const RootStack = createStackNavigator(
{
Home: { screen: HomeScreen },
},
{
initialRouteName: 'Home',
}
);

export default createAppContainer(RootStack);

If you run the snack in your device, you will see that the posts(fetchLeash() function) is working fine. But the fetchImage() is returning nothing.

My fetch-image.php file is here:

<?php
// Importing DBConfig.php file.
include 'DB.php';
 header('Content-Type: application/json');
// Creating connection.
 $con = mysqli_connect($HostName,$HostUser,$HostPass,$DatabaseName);

 // Getting the received JSON into $json variable.
 $json = file_get_contents('php://input');

 // decoding the received JSON and store into $obj variable.
 $obj = json_decode($json,true);

// Populate User email from JSON $obj array and store into $email.
$image = $obj['image'];
if($image == "") {
$blank[] = array("url"=>"");
echo json_encode($blank);
}else{

//query to get image url with the code received
$Sql_Query = "SELECT * FROM `leash_img` WHERE `pid`= '".$image."' ";

// Executing SQL Query.
$check = mysqli_query($con,$Sql_Query);


if($check){
while($row=mysqli_fetch_assoc($check)){

 $SuccessLoginMsg[] = array("url"=> $row['image']);

}
 // Converting the message into JSON format.
$SuccessLoginJson = json_encode($SuccessLoginMsg);


echo $SuccessLoginJson;
 }
}

?>

This returns like the following:

[{"url":"link here"}]

The PHP file is working fine. But the react native fetchImage() is not working.

I am totally new to react native. So forgive my problems.
I am just out of my ideas. Please help me.

3
  • 1
    When you use async function like fetch you need to await the result. On render function you try see the result from async function. async function return promise and you see nothing. Commented Jun 6, 2020 at 3:31
  • Is it ok that fetchImage expects on image but you pass item.images? Commented Jun 6, 2020 at 4:10
  • item.images is just a number which is sent to another API to fetch the url of image Commented Jun 6, 2020 at 10:13

1 Answer 1

1

You can't asynchronously render UI from the render function, you need to fetch the data outside it in one of the lifecycle functions and conditionally render UI while it is being fetched.

Once the data has been fetched you should go ahead and fetch the image urls. Use Promise.all and map each response item to a fetch request. This will allow all image url fetches to resolve asynchronously and maintain index order.

fetchLeash() {
  fetch('https://lishup.com/app/')
    .then((response) => response.json())
    .then((responseJson) => {
      this.setState({ data: responseJson });

      Promise.all(responseJson.map(({ images }) => this.fetchImage(images)))
        .then((images) => {
          this.setState({ imageUrls: images.map(url => ({ uri: url })) })
        });
    })
    .catch((error) => {
      Alert.alert('error!');
    })
    .finally(() => {
      this.setState({ Loading: false });
    });
}

The other important change is that the image response is an array of length 1, so need to access correctly.

fetchImage(image) {
  return fetch('https://lishup.com/app/fetch-image.php', {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ image }),
  })
    .then((response) => response.json())
    .then((responseJson) => responseJson[0].url);
}

Now you can conditionally render an Image if the url at that index exists.

renderLeash = ({ item, index }) => (
  <View>
    <Card
      style={{ height: 100, justifyContent: 'center', alignItems: 'center' }}>
      <ListItem
        leftAvatar={{
          title: item.user,
          source: { uri: item.userpic },
        }}
        title={item.user}
        subtitle={item.time}
        chevron
      />

      <Divider style={{ margin: 5, backgroundColor: 'white' }} />
      <HTML html={item.text} />
      <Text>
        {this.state.imageUrls[index] && this.state.imageUrls[index].uri}
      </Text>
      {this.state.imageUrls[index] && (
        <Image
          source={this.state.imageUrls[index]}
          style={{ width: 100, height: 100 }}
          PlaceholderContent={<ActivityIndicator />}
        />
      )}
    </Card>
  </View>
);

Expo Snack

EDIT Allow display of all fetched image URLs. Instead of grabbing and returning just the first URL, return an array of URLs. Below I mapped the URLs to a new array before returning them, and these can be set directly in state now. Update the render function to use an additional guard (array length check) and render null if array doesn't exist. (Could also use another FlatList here if you wanted to)

fetchLeash() {
  return fetch('https://lishup.com/app/')
    .then((response) => response.json())
    .then((responseJson) => {
      this.setState({ data: responseJson });

      Promise.all(
        responseJson.map(({ images }) => this.fetchImage(images))
      ).then((imageUrls) => this.setState({ imageUrls }));
    })
    .catch((error) => {
      Alert.alert('error!');
    })
    .finally(() => {
      this.setState({ Loading: false });
    });
}

fetchImage(image) {
  return fetch('https://lishup.com/app/fetch-image.php', {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ image }),
  })
    .then((response) => response.json())
    .then((responseJson) =>
      // Filter elements with empty string URLs, then app just the URL
      responseJson.filter(({ url }) => url).map(({ url }) => url)
    );
}

...

{this.state.imageUrls[index] && this.state.imageUrls[index].length
  ? this.state.imageUrls[index].map((uri) => (
      <Image
        source={{ uri }}
        style={{ width: 100, height: 100 }}
        PlaceholderContent={<ActivityIndicator />}
      />
    ))
  : null}
Sign up to request clarification or add additional context in comments.

5 Comments

But when there are multiple image urls returned from the next fetch, its only showing the first one. How can I show all the images?
@NothingtoSay Ah, in that case instead of returning a single url responseJson[0].url you can process and return an array of urls, and in the Promise.all then block map each url to a { uri: url } object. Then in the main component map them, i.e. this.state.imageUrls[index].length && this.state.imageUrls[index].map(({ uri }) => ... <Image source={uri} ...
believe me, i learn in a different path. i have read official react native docs. i am struggling with this for a month. and u solved it in a minute. but i have not been able to solve the next problem. i know you are busy. but can you please add the edits in your snack. that will help a 14 years old young developer a lot. big sorry for disturbing you third time
@NothingtoSay Snack updated. No worries. React (and by extension, react-native) has, IMO, a rather steep learning curve, especially if you come from the world of OOP (Object oriented Programming). It took me about half an internship before things just started to "click" into place, but this was with nearly zero experience in javascript, and certainly none in any JS frameworks going in. Just keep being curious and wanting to learn new things.
How can i give thanks to you now. You have done a lot for me. I would give anything to thank a teacher like you. Thanks aaaaaaa looootttttt. You helped me a lot^∞ . Have a nice day

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.