k-wasilewski
k-wasilewski

Reputation: 4623

React js - calling a function in render works BUT in componentDidMount doesn't

I'm new to React and have been struggling with this issue for the last 2 days. Why does the call to this.imgList() work when I put it in render (can't be there - performance issues), but doesn't when it's in componentDidMount?

So I set the state within the constructor:

class PersonalBananas extends React.Component {

    constructor(){
        super();
        this.state = {
            username: 0,
            images: 0,
            pred: 0,
            IMAGES: 0
        }
    }

Then in componentDidMount I have two ajax calls which work and the call to imgLis() which doesn't:



    componentDidMount() {
        axios.get('http://localhost:8081/auth/username')
            .then((response) => {
                let uname = response.data;
                this.setState({
                    username: uname
                });
            })

        axios.get('http://localhost:8081/auth/files')
            .then((response) => {
                let imgs = response.data;
                console.log("images response: "+imgs);
                this.setState({
                    images: imgs
                });
            })

        this.imgList();
    }

This method I call from my imgLis(), there are some ajax calls:


    getImgPred = (path) => {
        var username = this.state.username;
        var $this = this;

        let regex = new RegExp(username+'\/(.*?)$');
        let imgRegex= /{username}\/(.*?)$/;

        let filename = regex.exec(path);
        console.log("filename at front:"+filename[1]);
        console.log("regex1:"+imgRegex+", regex2:"+regex);

        axios.post('http://localhost:8081/auth/imgpred',
            "filename=" + filename[1]
        ).then(function (response) {
            console.log("response at front (get img prediction):"+response.data);
            if (response.status === 200) {
                $this.setState({
                    pred: response.data
                });
            }
        });
    }

Then there is this infamous imgLis(), which works like a charm when I call it directly from render, but now doesn't:


    imgList = () => {
        var $this = this;
        const IMAGES = [];
        const imgpaths = this.state.images;

        for (let i = 0; i < imgpaths.length; i++) {
            var path = imgpaths[i]
            this.getImgPred(path);
            console.log("pred:"+$this.state.pred);

            IMAGES.push({
                src: process.env.PUBLIC_URL +`/${path}`,
                thumbnail: process.env.PUBLIC_URL +`/${path}`,
                thumbnailWidth: 320,
                thumbnailHeight: 320,
                caption: $this.state.pred
            })
        }
        this.setState({
            IMAGES: IMAGES
        })
    };

And in my render() I try to get the IMAGES -doesn't work, BUT it works when I replace {this.state.IMAGES} with {this.imgLis()} and in imgLis() just return IMAGES...


    render() {

        return (
            <div>
                <Gallery images={this.state.IMAGES}/>
            </div>
        )
    }
}

export default PersonalBananas;

Any help?

Upvotes: 1

Views: 189

Answers (2)

Anurag Srivastava
Anurag Srivastava

Reputation: 14413

In componentDidMount, use a callback function after this.setState like so:

axios.get('http://localhost:8081/auth/files')
.then((response) => {
    let imgs = response.data;
    console.log("images response: "+imgs);
    this.setState({
      images: imgs
    }, () => {
      this.imgList();
    });
})

The reason being that this.imgList() uses this.state.images, but this.setState() is not synchronous in React. Since render() is called after every this.setState() your function worked there, because this.state.images is defined but not in componentDidMount()

Upvotes: 2

jimmy5312
jimmy5312

Reputation: 429

componentDidMount() run once, but you have async API calls (axios) inside it. When this.imgList() is called in componentDidMount(), most probably you had not get your response back yet.

Simple fix is

axios('http://localhost:8081/auth/files')
  .then((response) => {

    let imgs = response.data;
    console.log("images response: "+imgs);
    this.setState(
      {images: imgs},
      this.imgList // Will run after setState is completed
    );
  })

Upvotes: 0

Related Questions