Dylan
Dylan

Reputation: 362

How do I force a component to re-render in between synchronous requests? (React Native)

My goal is to make a series of requests, and upon the completion of each request, update text that indicates what step is currently being processed. Like so:

Screenshot

Before I get into specifics I'd like to address that as far as I know there are two ways to trigger a re-render

  1. Use setState
  2. Use forceUpdate (not recommended)

Neither of these methods are working in my case which is why I'm creating this question.

I read, as far as I understand, that updating state in async requests has issues with triggering re-renders. So I tried to process each async request synchronously with the recursive method processRequests at the bottom here:

process = () => {
    this.setState( { processing: true } );

    const dir = RNFetchBlob.fs.dirs.MainBundleDir;

    const { item } = this.state;

    const requests = [
        {
            url: `${this.base_url}/${item.id}`,
            message: 'Loading model...',
            callback: function( res ) {
                return new Promise( resolve => {
                    const model_url = JSON.parse( res.data );
                    RNFetchBlob.config( {
                        path : `${dir}/model.obj`
                    } ).fetch( 'GET', model_url ).then( () => resolve() )
                } );
            }
        },
        {
            url: `${this.base_url}/${item.id}`,
            message: 'Loading texture...',
            callback: function( res ) {
                return new Promise( resolve => {
                    const texture_url = JSON.parse( res.data );
                    const split = texture_url.split( '.' );
                    const extension = split[ split.length -1 ];
                    RNFetchBlob.config( {
                        path : `${dir}/texture.${extension}`
                    } ).fetch( 'GET', texture_url ).then( () => resolve() )
                } );
            }
        }
    ];

    this.processRequests( requests );
};

requestFileAndDownload = ( url, callback ) => {
    return new Promise( resolve => {
        RNFetchBlob.fetch( 'GET', url ).then( res => {
            callback( res ).then( () => {
                resolve();
            } )
        } );
    } );
};

processRequests = requests => {
    const request = requests.shift();

    const { url, message, callback } = request;

    console.log( 'message = ', message );

    this.setState( { processing_text: message } );

    this.requestFileAndDownload( url, callback ).then( () => {
        if( requests.length ) {
            this.processRequests( requests );
        }
    } );
};

The files are successfully downloaded but I cannot get the component to re-render in between requests. Even though the text in the state has definitely been updated, as indicated by console messages I've output:

Screenshot

I tried inserting forceUpdate in a bunch of different places, but I find in general that if I ever get to the point where I feel the need to try and use forceUpdate it never works.

Upvotes: 0

Views: 758

Answers (1)

You should try to use the setState callback handler function in the processRequests call.

It will wait until setState finishes and the component re-renders before calling the next file download function.

this.setState( { processing_text: message }, () => {
  this.requestFileAndDownload( url, callback ).then( () => {
    if( requests.length ) {
        this.processRequests( requests );
    }
  });
});

Upvotes: 3

Related Questions