0

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?

0

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

First of all I wanna tell you, your code worked perfectly. But left me with lots of questions. One of the main questions is why you preferred to use of Async/Await and Promises? I'm not familiar with the use of Async/Await so would like to know more about it and why you thought it'll perfectly fit to my use case?
Async/Await will keep the code more in sequence and you can see right at the beginning, if these method uses promises. With the old then,catch style the code is more nested, although it behaves like a sequence. And for me more complicated to read. But it's totally fine if you use the normal promises.
You said, it keep the code in sequence but I didn't get in what sense? As per my understanding asynchronous function not run in sequential way. May you're refering to different context. Please clear my confusion.
0

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

2 Comments

It's throwing an error at promises.push(this.fetchEventImage(this.state.images[i].image_path)) this line.
Thank you for your efforts @Talgat and thank you for taking interest in my question. Answer by Auskennfuchs is worked for me.

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.