我正在构建一个计时器应用,该应用具有按顺序运行的多个计时器。我有一个计时器组件(UnitTimer
),该计时器组件基于我的应用状态中的值(numTimers
)被循环渲染。每次用户添加新计时器numTimers
都会增加通过setState。问题在于每个计时器都在其状态下保存有关其自身的信息(例如应运行多长时间)。当用户添加新计时器时,以前的计时器会奇怪地忘记将其设置为什么时间,但要记住例如其名称。我很困惑,不知道为什么。
应用程序:
class App extends React.Component {
constructor (props) {
super(props);
this.state = {
timer: [],
timerOn: false,
timerHasStarted: false,
appHasBeenReset: false,
// timerOn:false + timerHasStarted:true = paused
numTimers: 1,
activeTimerId: 0,
totalTime: '',
timeHasExpired: false,
succesfullEnding: false
};
}
addNewTimer = () => {
this.setState(prevState => {
return { numTimers: prevState.numTimers + 1 };
});
};
removeTimer = () => {
this.setState(prevState => {
return { numTimers: prevState.numTimers - 1 };
});
};
renderTimerManagementButtons = () => {
let buttons = [];
//allow max 6 timers
if (this.state.numTimers < 6) {
buttons.push(
<Button className="timerManagementButton" key={6} onClick={() => this.addNewTimer()}>+</Button>
);
}
//show remove button as soon as one was added
if (this.state.numTimers > 1) {
buttons.push(
<Button className="timerManagementButton" key={7} onClick={() => this.removeTimer()}>-</Button>
);
}
return buttons;
};
renderTimers = () => {
let timers = [];
for (let counter = 0; counter < this.state.numTimers; counter++) {
let key = counter + 1;
timers.push(
<UnitTimer key={key}
startNextTimer={this.startNextTimer}
isActive={key === this.state.activeTimerId}
timerIsPaused={key === this.state.activeTimerId && !this.state.timerOn}
appHasBeenReset={this.appHasBeenReset}
/>
);
}
return timers;
};
startNextTimer = () => {
let i = this.state.activeTimerId;
if (this.state.numTimers > 1) {
this.setState({
activeTimerId: i + 1
});
} else if (this.state.numTimers === this.state.activeTimerId) {
this.timeExpired();
} else if (this.state.numTimers === 1) {
this.timeExpired();
}
};
startTimer = () => {
this.setState({
activeTimerId: 1,
timerOn: true,
timerHasStarted: true
});
};
resumeTimer = () => {
this.setState({
timerOn: true
});
};
pauseTimer = () => {
this.setState({
timerOn: false
});
};
resetApp = () => {
this.setState({
timerOn: false,
timerHasStarted: false,
appHasBeenReset: true,
activeTimerId: 0
});
};
finishMeeting= () => {
this.setState({
timerOn: false,
timerHasStarted: false,
appHasBeenReset: true,
activeTimerId: 0,
succesfullEnding: true
});
};
timeExpired = () => {
this.setState({
timerOn: false,
timerHasStarted: false,
appHasBeenReset: true,
activeTimerId: 0,
timeHasExpired: true,
succesfullEnding: false
});
};
printState = () => {
console.log(this.state);
};
renderButtons = () => {
let buttons = [];
if (this.state.timerOn === false && this.state.timerHasStarted === false) {
buttons.push(
<Button key={1} id="startButton" className="button greenButton" onClick={this.startTimer}>Start Meeting</Button>
);
}
if (this.state.timerOn === true) {
buttons.push(
<Button key={2} id="pauseButton" className="button pauseButton" onClick={this.pauseTimer}> </Button>,
<div className="spacer25px" />,
<Button key={4} id="finishButton" className="button greenButton" onClick={this.finishMeeting}>Launch Kitties</Button>,
<div className="spacer25px" />,
<Button key={3} id="resetButton" className="button textButtonWhite" onClick={this.resetApp}>Reset</Button>
);
}
if (this.state.timerOn === false && this.state.timerHasStarted === true) {
buttons.push(
<Button key={5} id="resumeButton" className="button resumeButton" onClick={this.resumeTimer}> </Button>,
<div className="spacer25px" />,
<Button key={7} id="finishButton" className="button greenButton" onClick={this.finishMeeting}>Launch Kitties</Button>,
<div className="spacer25px" />,
<Button key={6} id="resetButton" className="button textButtonWhite" onClick={this.resetApp}>Reset</Button>
);
}
return buttons;
}
renderContent = () => {
let content = [];
console.log("I got called");
if (!this.state.timeHasExpired && !this.state.succesfullEnding) {
console.log("If statement true");
content.push(
<div className="noFlexShrink">
<div className="controlsDisplay" />
<div className="spacer25px" />
{this.renderTimers()}
<div className="timerManagementButtonDiv">
{this.renderTimerManagementButtons()}
<p className="subtilte noMargin">Agenda Items</p>
</div>
<div className="centerBox">
{this.renderButtons()}
</div>
</div>
);
} else if (this.state.succesfullEnding && !this.state.timeHasExpired) {
content.push(
<div className="noFlexShrink">
<h1 className="center white ">Congrats Comrades</h1>
</div>
);
} else if (!this.state.succesfullEnding && this.state.timeHasExpired) {
content.push(
<div className="noFlexShrink">
<h1 className="center white ">100.000.000 Dead, because of you.</h1>
</div>
);
}
return content;
};
render () {
return (
<>
<div className="appContainer">
<div className="header">
<h4 className="white montserrat">KITTEN TIMER</h4>
</div>
<div className="centerBox">
{this.renderContent()}
<Button className="button greenButton" onClick={this.printState}>Print</Button>,
</div>
</div>
</>
);
}
}
export default App;
UnitTimer:
class UnitTimer extends Component {
componentWillReceiveProps (nextProps) {
if (nextProps.isActive && !this.props.isActive && !nextProps.timerIsPaused && !this.state.timerOn) {
this.startTimer();
} else if (this.props.isActive && nextProps.timerIsPaused) {
this.stopTimer();
} else if (this.props.isActive && !nextProps.timerIsPaused && nextProps.isActive && !this.state.timerOn) {
this.startTimer();
} else if (!nextProps.isActive) {
this.resetTimer();
} else if (nextProps.appHasBeenReset || this.props.appHasBeenReset) {
this.resetTimer();
}
}
state = {
timerOn: false,
timerStart: 0,
timerTime: 300000,
timerTitle: ''
};
printState = () => {
console.log(this.state)
console.log(this.props)
}
startTimer = () => {
this.setState({
timerOn: true,
timerTime: this.state.timerTime,
timerStart: this.state.timerTime
});
this.timer = setInterval(() => {
const newTime = this.state.timerTime - 10;
if (newTime >= 0) {
this.setState({
timerTime: newTime
});
} else {
clearInterval(this.timer);
this.setState({ timerOn: false });
this.props.startNextTimer();
}
}, 10);
};
stopTimer = () => {
clearInterval(this.timer);
this.setState({ timerOn: false });
};
handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
resetTimer = () => {
clearInterval(this.timer);
this.setState({
timerOn: false,
timerTime: 300000
});
};
restartTimer = () => {
clearInterval(this.timer);
this.setState({
timerOn: true,
timerStart: 0
});
};
adjustTimer = input => {
const { timerTime, timerOn } = this.state;
if (!timerOn) {
if (input === 'incMinutes' && timerTime + 60000 < 216000000) {
this.setState({ timerTime: timerTime + 60000 });
} else if (input === 'decMinutes' && timerTime - 60000 >= 0) {
this.setState({ timerTime: timerTime - 60000 });
}
}
};
jumpTimer = () => {
this.props.startNextTimer();
};
renderButtons = () => {
let buttons = [];
if (!this.props.isActive) {
buttons.push(
<div key={1} className="timeAdjustmentButtonsDiv">
<Button className="incMinutes" key={1} onClick={() => this.adjustTimer('incMinutes')}> </Button>
<Button className="decMinutes" key={2} onClick={() => this.adjustTimer('decMinutes')}> </Button>
</div>
);
} else if (this.props.isActive && this.state.timerTime < this.state.timerStart) {
buttons.push(
<Button className="button doneButton"key={1} onClick={() => this.jumpTimer()}>Done</Button>,
//<button key={2} onClick={() => this.restartTimer()}>Restart</button>
);
}
return buttons;
};
render () {
const { timerTime } = this.state;
let seconds = ('0' + (Math.floor((timerTime / 1000) % 60) % 60)).slice(-2);
let minutes = ('0' + Math.floor((timerTime / 60000) % 60)).slice(-2);
//let hours = ('0' + Math.floor((timerTime / 3600000) % 60)).slice(-2);
let cssClass = (this.props.isActive) ? "unitTimerActive" : "unitTimer";
return (
<div className={cssClass} >
<Input
className={(this.props.isActive ? "inputActive" : "inputOnDark")}
type="text"
placeholder="Agenda Item"
name="timerTitle"
value={this.state.timerTitle}
onChange={this.handleChange}
/>
<div className={(this.props.isActive ? "timeActive" : "time")} id={(this.state.timerTime < 60000 ? "dangerClose" : " ")}>
{minutes} : {seconds}
{this.renderButtons()}
</div>
</div>
);
}
}
export default UnitTimer;
UI:
预先感谢