R. Kohlisch
R. Kohlisch

Reputation: 2993

React Loading Spinner + Redux?

The UploadImage component is a dumb component. It gets the redux action to call for uploading, and it gets the image file path which that action puts into the appropriate reducer once the image is up. Here it is used with both props:

<UploadImage onUpload={this.props.uploadNewArticleImage} image={this.props.newArticleBuffer.image}/>

My newArticleBuffer-reducer has image: null, and once the upload is completed it will get the path to the image. When this happens, my component reacts, by not displaying the spinner anymore.

Internally, UploadImage looks like this:

import React, { Component } from 'react';

export default class UploadImage extends Component {
  constructor(props) {
    super(props);

    this.state = { file: "", loading: false};

    this.onSubmit = this.onSubmit.bind(this);
    this.onFileChange = this.onFileChange.bind(this);    
  }

  onSubmit(e) {
    e.preventDefault();
    this.setState({loading: true});
    this.props.onUpload(this.state.file);
  }

  onFileChange(event) {
    this.setState({ file: event.target.files[0]});
  }

  render() {    
    let spinner = <div>Not Loading</div>
    if(this.props.image == null && this.state.loading) {
      spinner = <div>Loading</div>;
    }

    return (
      <div>Image Upload Component
      <input 
        type="file" 
        accept="image/*"
        onChange={this.onFileChange} />

      {spinner}
       <button onClick={(e) => this.onSubmit(e)}>Upload Image</button>
      </div>
    );
  }
}

There are a few problems. First, I never set 'loading' back to false: I need to somehow do this when the Redux action is completed, but I don't know how to do this.

Second, and more importantly, this approach won't work when a user uploads an image, but then decides to upload a different file instead. image won't be null anymore.

Upvotes: 0

Views: 2775

Answers (2)

sme
sme

Reputation: 4153

First, connect the UploadImage component to the redux store.

Then, create some actions in your actions file:

// You only need to export and call this action from your component:
export const uploadNewArticleImage = (uploadData) => {
    return (dispatch) => {
        dispatch(uploadNewArticleImageLoading(true));   // set isLoading = true

        fetch('uploadData.url', {
            method: 'POST',
            body: uploadData.data,
            etc...
        })
            .then((response) => dispatch(uploadNewArticleImageLoading(false)))  // response received
            .then((response) => response.JSON())
            .then((jsonObj) => dispatch(uploadNewArticleImageSuccess(jsonObj.image))) // success
            .catch((error) => dispatch(uploadNewArticleImageLoading(false));          // error
    };
};

const uploadNewArticleImageLoading = (isLoading) => {
    return {
        type: "UPLOAD_NEW_ARTICLE_IMAGE_LOADING",
        payload: {
            isLoading: isLoading
        }
    };
};

const uploadNewArticleImageSuccess = (image) => {
    return {
        type: "UPLOAD_NEW_ARTICLE_IMAGE_SUCCESS",
        payload: {
            image: image
        }
    };
};

Then in your reducer, just handle the actions to update the store.

This way, isLoading will be set to false whenever we receive a response from the server, or an error occurs during the image upload.

Upvotes: 1

Tim B James
Tim B James

Reputation: 20364

What I have is that within Redux I have a PageState object that has various shared properties on it, one of these being isProcessing. Whenever there is an ajax call, another property called processingCount is incremented, and then isProcessing is set to processingCount > 0;

Whenever an ajax request is then completed or failed, the processingCount property is decremented.

Your PageState object can then be used within a Spinner component, can be attached to Buttons to stop disable them while there is processsing going on, or you can show various loading animations throughout the app.

So in your code, I would remove all component state, and move this into your Redux store.

Upvotes: 0

Related Questions