Rohit Sawai
Rohit Sawai

Reputation: 839

How to display dynamically fetched image on client end in react?

I'm working of Gallery feature of my website where I wanna show all images to client. I have successfully fetched images from server to client end in the form of blob. Images also display but on second attempt. I need to visit that gallery page for first time for fetching images then I need to visit any page and re-visit that gallery page for displaying those images.

Here is my code. Kindly let me know where I'm making mistake.

Code of main Gallery Component

constructor(props) {
    super(props);

    this.state = {
        eventDetail: [],
        images: [],
        imagesInBlob: [],
        url: ''
    }
    this.getAllEventsData = this.getAllEventsData.bind(this);
}

componentDidMount() {
    this.getAllEventsData();
}

getAllEventsData() {
    axios({
        method: 'POST',
        url: '/api/Account/GetAllEventsData',
        contentType: 'application/json',
        data: {},
    }).then(function (response) {
        console.log(response);
        this.setState({
            eventDetail: response.data
        });
        this.getAllImages();
    }.bind(this))


}

getAllImages() {
    axios({
        method: 'POST',
        url: '/api/Account/GetAllEventImages',
        contentType: 'application/json',
        data: {},
    }).then(function (response) {
        console.log(response);
        this.setState({
            images: response.data
        });
        this.getImageBlobs();
    }.bind(this))
}

getImageBlobs() {

    console.log("Image Objects:", this.state.images);

    for (var i = 0; i < this.state.images.length; i++) {
        this.fetchEventImage(this.state.images[i].image_path);
    }
    this.setState({
        imagesInBlob: imageArr
    });
    console.log("Images in Blob:", this.state.imagesInBlob);
}

fetchEventImage(imagePath) {
    var path = {
        ImagePath: imagePath
    }

    axios({
        method: 'POST',
        url: '/api/ImageFetch/FetchEventImages',
        responseType: 'blob',// important
        headers: {
            'Content-Type': 'application/json'
        },
        data: path
    }).then(function (response) {

        var re = /(?:\.([^.]+))?$/;
        const url = window.URL.createObjectURL(new Blob([response.data]));

        imageArr[img_arr_cnt++] = url;
        console.log("URL: ", url);

    }).catch(error => {
        console.log("Status:", error.response.status);
        console.log("Data:", error.response.data);
    });
}

handleImageDisplay() {
    var imgTag = "<img src=" + this.state.imagesInBlob[0] + '" />';
    return imgTag;
}

render() {
    return (
        <>
            <section id="single-project" className="section">
                <div className="container row justify-content-center">
                    <div className="col-lg-12">
                        <div className="row justify-content-center">
                            <div className="col-lg-6 project-info">
                                <h3>Meeting on 17th May 2019</h3>
                                <DisplayComp propName={this.state.imagesInBlob[0]} />
                                <img src={this.state.imagesInBlob[0]} />
                            </div>

                        </div>
                    </div>
                </div>
            </section >
        </>
    );
}

DisplayComp component code

constructor(props){
    super(props);
}


render(){
    return (
        <img src={this.props.propName} alt="image"/>
    );  
}

Please let me know where I'm making mistake and how can I display it on client end?

Upvotes: 0

Views: 1547

Answers (2)

Talgat Saribayev
Talgat Saribayev

Reputation: 1938

The issue is with fetchEventImage. You are not setting state, no this.setState. That is why your component does not rerenders. Try Promise.all.

getImageBlobs() {

    console.log("Image Objects:", this.state.images);

   const promises
    for (var i = 0; i < this.state.images.length; i++) {
         promises.push(this.fetchEventImage(this.state.images[i].image_path));
    }
    Promises.all(promises).then(imagesInBlob => {
    this.setState({
        imagesInBlob
    });
    })
}

fetchEventImage(imagePath) {
    var path = {
        ImagePath: imagePath
    }

    return axios({
        method: 'POST',
        url: '/api/ImageFetch/FetchEventImages',
        responseType: 'blob',// importaqnt
        headers: {
            'Content-Type': 'application/json'
        },
        data: path
    }).then(function (response) {

        var re = /(?:\.([^.]+))?$/;
        const url = window.URL.createObjectURL(new Blob([response.data]));

        return url
        console.log("URL: ", url);

    }).catch(error => {
        console.log("Status:", error.response.status);
        console.log("Data:", error.response.data);
    });
}

Sorry for any style issues, I answered on my phone

Upvotes: 0

Auskennfuchs
Auskennfuchs

Reputation: 1737

There's a lot going on in your code. You've some problems with asynchronous calls and also not all functions are bound to this. I would suggest to convert your code to async/await to make it more readable and use named functions, because they are automatically bound to this.

getAllEventsData = async () => {
    const response = await axios({
        method: 'POST',
        url: '/api/Account/GetAllEventsData',
        contentType: 'application/json',
        data: {},
    })
    console.log(response);
    this.setState({
            eventDetail: response.data
        });
    this.getAllImages()
}

In getAllImages it's neccessary to wait for setState to be finished, because getImageBlobs is using the state value and setState is asynchronous.

getAllImages = async () => {
    const response = await axios({
        method: 'POST',
        url: '/api/Account/GetAllEventImages',
        contentType: 'application/json',
        data: {},
    })
    console.log(response);
    await this.setState({
            images: response.data
        });
    this.getImageBlobs();
}

In getImageBlobs there is a loop over a asynchronous function. So you need to wait till all calls are finished. Promise.all will help here. Also imageArr is some magically created global variable which is bad practise. Better return the value from fetchEventImage and collect it in a local array. I replaced the for-loop with a map, which basically does the same, but looks cooler and cleaner and returns an array with all the Blob-URLs as Promise. Also destructuring images from this.state helps cleaning up the code.

getImageBlobs = async () => {
    const { images } = this.state
    console.log("Image Objects:", images);
    const imageArr = await Promise.all(
                        images.map(image => this.fetchEventImage(image.image_path))
    this.setState({
        imagesInBlob: imageArr
    });
    console.log("Images in Blob:", this.state.imagesInBlob);
}

Also in fetchEventImage async/await looks cleaner and with try/catch you can do the same for error handling. As stated, this function will now return the created Blob-URL.

fetchEventImage = async (imagePath) => {
    var path = {
        ImagePath: imagePath
    }
    try {
      const response = await axios({
          method: 'POST',
          url: '/api/ImageFetch/FetchEventImages',
          responseType: 'blob',// important
          headers: {
              'Content-Type': 'application/json'
          },
          data: path
      })
      const url = window.URL.createObjectURL(new Blob([response.data]));
      console.log("URL: ", url);
      return url
    } catch(error) {
        console.log("Status:", error.response.status);
        console.log("Data:", error.response.data);
    }
}

I haven't tested the refactored code. Maybe there are some minor mistakes, but I think in general it should work.

Upvotes: 2

Related Questions