Reputation: 2528
I have created a generic component for showing progress. It takes a prop 'type' to render the type of progress. The types are 'bar' progress and 'circular' progress. The bar progress displays and when i click an Accordion shows circular progress like this:
What I want is that if I click pause on any progress(bar or circular), both the progress should stop. Here is the code for this generic progress component:
import React, {Component} from 'react';
import CircularProgressBar from 'react-circular-progressbar';
import config from '../../config';
import './Progress.css';
import './ProgressCircular.css';
class GenericProgress extends Component {
constructor(props) {
super(props);
this.state = {
progressPercent: props.progress,
width: "100%",
startTime: props.startTime,
progressStatus: props.status,
extractId: props.extractId,
};
this.tick=this.tick.bind(this);
}
tick() {
const reqObj={
"op": "progress",
"extractID" : this.props.extractId,
"last_ts" : this.state.last_ts,
"progress": this.state.progressPercent,
};
fetch(`${config.apiHost}/extracttool/extract`,
{
method: 'POST',
body: JSON.stringify(reqObj),
headers: {
'Content-Type': 'application/json'
}
}
).then((response) => {
return response.json();
}).then((data) => {
if(this.state.progressStatus !== 'Paused' ) {
const progressCounter = data.payload.progress;
const last_ts = data.payload.last_ts;
if (progressCounter >= 100) {
this.props.changeExecutionStatus('Complete');
this.setState({...this.state, progressPercent: 100, progressStatus: 'Complete'});
clearInterval(this.timerID);
} else {
this.setState({
...this.state,
progressPercent: progressCounter,
last_ts: last_ts
});
}
}
});
}
callApi = (reqObj, status) => {
fetch(`${config.apiHost}/extracttool/extract`,
{
method: 'POST',
body: JSON.stringify(reqObj),
headers: {
'Content-Type': 'application/json'
}
}
).then((response) => {
return response.json();
}).then((data) => {
this.setState({
progressStatus: status
});
});
}
componentDidMount() {
if (this.state.progressStatus === 'Progress' ) {
this.startTimer();
}
}
onPause = () => {
this.props.changeExecutionStatus('Paused');
clearInterval(this.timerID);
const reqObj={
op: "flow_control",
extractID: this.props.extractID,
value: "pause"
};
this.callApi(reqObj, 'Paused');
}
startTimer = () => {
this.timerID = setInterval(
() => this.tick(),
2500
);
}
onResume = () => {
this.props.changeExecutionStatus('Progress');
const reqObj={
op: "flow_control",
extractID: this.props.extractId,
value: "resume"
};
this.callApi(reqObj, 'Progress');
this.startTimer();
}
onCancel = () => {
this.props.changeExecutionStatus('Cancelled');
clearInterval(this.timerID);
const reqObj={
op: "flow_control",
extractID: this.props.extractId,
value: "cancel"
};
this.callApi(reqObj, 'Cancelled');
}
componentWillUnmount() {
clearInterval(this.timerID);
}
render() {
const { progressStatus, progressPercent, startTime } = this.state;
let progressClass = progressStatus === 'Complete' ? 'progress-bar progress-bar-success' : 'progress-bar';
if ( progressStatus === 'Paused' ) {
progressClass = 'progress-bar-warning progress-bar';
} else if( progressStatus === 'Cancelled' ) {
progressClass = 'progress-bar-danger progress-bar';
}
return (
<div className="progress-bar-container">
{
this.props.type === 'bar' &&
<div>
<div className="progress">
<span className="progressStartTime">Start Time: {startTime}</span>
<div
className={progressClass}
role="progressbar"
aria-valuenow={ progressPercent }
aria-valuemin="0"
aria-valuemax="100"
style={{width: progressPercent + "%"}}
>
</div>
</div>
<span className="extractProgress">{progressPercent < 100 ? progressStatus + ': '+this.state.progressPercent + '%' : 'Complete'}</span>
{
progressStatus === 'Paused' &&
<span className="playIcon" onClick={this.onResume}> </span>
}
{
progressStatus === 'Progress' &&
<span className="pauseIcon" onClick={this.onPause}> </span>
}
{
progressStatus !== 'Complete' && progressStatus !== 'Cancelled' &&
<span className="cancelIcon" onClick={this.onCancel}> </span>
}
</div>
}
{
this.props.type === 'circular' &&
<div>
<div className="CircularProgress">
{
progressStatus === 'Paused' &&
<span className="playIcon" onClick={this.onResume}> </span>
}
{
progressStatus === 'Progress' &&
<span className="pauseIcon" onClick={this.onPause}> </span>
}
<CircularProgressBar percentage={progressPercent} />
{
progressStatus !== 'Complete' && progressStatus !== 'Cancelled' &&
<span className="cancelIcon" onClick={this.onCancel}> </span>
}
</div>
</div>
}
</div>
);
}
}
export default GenericProgress;
And here is the component where I am calling these progress bar and circular:
import React from 'react';
import { Panel, Row } from 'react-bootstrap';
import {Link} from 'react-router-dom';
import GenericProgress from './GenericProgress';
import LogFile from './LogFile';
import moment from 'moment'
import './Extract.css';
class Extract extends React.Component {
constructor(props) {
super(props);
this.state = {
open: props.isOpen ? true : false,
executionStatus: this.props.data.execution_status
}
this.changeExecutionStatus = this.changeExecutionStatus.bind(this);
}
componentWillReceiveProps(newProps) {
if(this.props !== newProps){
if(this.state.executionStatus !== this.props.execution_status) {
console.log(this.state.executionStatus);
this.changeExecutionStatus(this.state.executionStatus);
}
}
}
changeExecutionStatus(status) {
this.setState({
executionStatus: status
})
}
render() {
const {name, progress, start_time, end_time, execution_status, id, engagement} = this.props.data;
const start_date_time = moment(start_time).format('MMMM Do YYYY, h:mm:ss a');
const end_date_time = moment(end_time).format('MMMM Do YYYY, h:mm:ss a');
const startTime = start_date_time.split(',')[1];
const startDate = start_date_time.split(',')[0];
const endTime = end_date_time.split(',')[1];
const endDate = end_date_time.split(',')[0];
return (
<div className="extract">
<div>
<span className={ this.state.open ? "arrowUpIcon" : "arrowDownicon" } onClick={() => {this.setState({open: !this.state.open})}}></span>
<h4>
{
this.props.clientDetails ?
<Link to={{
pathname: '/client/'+this.props.clientId,
state: {
extractId: id,
engagementId: engagement,
source: 'extractDirect'
}
}} >{name}</Link>
:
name
}
</h4>
<div className="progressBar">
<GenericProgress
type="bar"
progress={progress}
startTime={start_time}
status={this.state.executionStatus}
extractId={id}
changeExecutionStatus={this.changeExecutionStatus} />
</div>
<Panel collapsible expanded={this.state.open}>
<div>
<Row>
<div className="col-lg-3">
<div>
<GenericProgress
type="circular"
progress={progress}
startTime={start_time}
status={this.state.executionStatus}
extractId={id}
changeExecutionStatus={this.changeExecutionStatus} />
</div>
<br/>
<div>
<b>Start Time:</b> {startTime}
<br/>
<b>Start Date:</b> {startDate}
<br/><br/><br/>
<b>End Time:</b> {endTime}
<br/>
<b>End Date:</b> {endDate}
</div>
</div>
<div className="col-lg-9">
<LogFile
startDate={startDate}
startTime={startTime}
status={execution_status}
/>
</div>
</Row>
</div>
</Panel>
</div>
</div>
);
}
}
export default Extract;
Upvotes: 1
Views: 1221
Reputation: 2730
Now you have two source of truth
. The progress status
in parent component and progress status
in each of Progress
Components.
You should make the Progress
Component really dumb. It should only render given props.
Move your fetch
logic in parent component and change the progress status from it.
Upvotes: 1
Reputation: 1465
From what I can see in your code, you need to keep track of progress in your parent component (Extract
component)
The logic would be:
Extract
component set the initial value of progress value (either playing/stopped or based on this.props.data
)GenericProgress
component as propFrom your code, I think you did something similar, but you are inconsistent with the value of progressStatus
- you are setting it from props initially (in the constructor), and later on in the code setting different value based on the response from the API
Upvotes: 0