Reputation: 2993
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
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
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