简体   繁体   中英

React Native CameraRoll.getPhotos API doesn't render the results

Hi I have the following class where I am trying to get the photos from camera roll and display it.

class CameraRollProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
     images: []
    };
  }

  componentWillMount() {
    const fetchParams = {
      first: 25,
    };
    CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError);
  }

  storeImages(data) {
    const assets = data.edges;
    const images = assets.map((asset) => asset.node.image);
    this.state.images =  images;
  }

  logImageError(err) {
    console.log(err);
  }

  render() {
      return (
        <ScrollView style={styles.container}>
          <View style={styles.imageGrid}>
            { this.state.images.map((image) => <Image style={styles.image} source={{ uri: image.uri }} />) }
          </View>          
        </ScrollView>
      );
  }
};

export default CameraRollProject;

The issue is my render function is getting called before my CameraRoll.getPhotos promise get resolved. So I don't get any photos.

To solve this issue I changed my program into following

render() {
      return CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError)
        .then(() => {
          return (
            <ScrollView style={styles.container}>
              <View style={styles.imageGrid}>
                { this.state.images.map((image) => <Image style={styles.image} source={{ uri: image.uri }} />) }
              </View>          
            </ScrollView>
          );
        });
  }

However this give me the following error

错误画面

What can I do in above situation? How can I make sure the render only works after the CameraRoll.getPhotos get resolved.

So I resolved this issue. The main reason for my problem was I was not using CameraRoll.getPhotos properly as a Promise. I was passing incorrect parameter inside the function. To solve this I got rid of the following functions

 storeImages(data) {
    const assets = data.edges;
    const images = assets.map((asset) => asset.node.image);
    this.state.images =  images;
  }

  logImageError(err) {
    console.log(err);
  }

And make my CameraRoll.getPhotos like the following

CameraRoll.getPhotos({first: 5}).then(
  (data) =>{
    const assets = data.edges
    const images = assets.map((asset) => asset.node.image);
        this.setState({
          isCameraLoaded: true,
          images: images
        })
  },
  (error) => {
     console.warn(error);
  }
);

Here is my complete code to get pictures from CameraRoll in react-native just in case anyone interested

class CameraRollProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
     images: [],
     isCameraLoaded: false
    };
  }

  componentWillMount() {
    CameraRoll.getPhotos({first: 5}).then(
      (data) =>{
        const assets = data.edges;
        const images = assets.map((asset) => asset.node.image);
        this.setState({
          isCameraLoaded: true,
          images: images
        })
      },
      (error) => {
        console.warn(error);
      }
    );
  }

  render() {
      if (!this.state.isCameraLoaded) {
        return (
          <View>
            <Text>Loading ...</Text>
          </View>
          );
      }
      return (
        <ScrollView style={styles.container}>
          <View style={styles.imageGrid}>
            { this.state.images.map((image) => <Image style={styles.image} source={{ uri: image.uri }} />) }
          </View>          
        </ScrollView>
      );
  }
};

export default CameraRollProject;

I think you should use react-native-image-picker

You have many parameters to retrieve a picture as you wish

  selectPhotoTapped() {
    const options = {
      title: 'Choose a picture',
      cancelButtonTitle: 'Back',
      takePhotoButtonTitle: 'Take a picture...',
      chooseFromLibraryButtonTitle: 'Choose from my pictures..',
      quality: 1,
      maxWidth: 300,
      maxHeight: 300,
      allowsEditing: true,
      mediaType: 'photo',
      storageOptions: {
        skipBackup: true
      }
    }

it is much easier to handle than CameraRollProject, and the documentation is very well explained. for what you would do it suits perfectly. (It works on iOS and Android)

One way to do this would be to use a ListView rather than a Scrollview because you can utilize a datasource . Here is a sample of how you could do this:

constructor(props) {
  super(props);

  const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
  this.state = { dataSource: ds.cloneWithRows([]) };

  this.loadPhotos();
}

loadPhotos() {
  const fetchParams = {
    first: 25,
  };

  CameraRoll.getPhotos(fetchParams).then((data) => {
    this.state.dataSource.cloneWithRows(data.edges);
  }).catch((e) => {
    console.log(e);
  });
}

This way, you render an empty list (and a loading state for good UX) and then once the fetch has completed you set the data in the ListView .

If you want to stick with the ScrollView and the mapped images, you would also need some sort of loading state until the photos load. Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM