Reputation: 6586
I added an mp3 to the src folder in a project bootrstrapped with Create React App. I added a component for the audio file, Audio.js, which I'd like to play conditionally based on whether a prop playAlarm
is true or not.
The parent App.js passes the prop to child Timer.js, which renders Audio.js.
The Audio.js file is giving me a 'React' is defined but never used error, and I'm not sure why.
Audio.js:
import React, { Component } from 'react';
class Audio extends Component {
constructor() {
super();
this.url = "./chime.mp3";
this.audio = new Audio(this.url);
}
render() {
return (
this.audio
);
}
}
export default Audio;
In Timer.js, Audio is rendered like this: {props.playAlarm ? <Audio /> : null}
When I tested playing the audio, when playAlarm is set to true I get Uncaught RangeError: Maximum call stack size exceeded at the line with super() in Audio.js.
App.js:
import React, { Component } from 'react';
import Timer from './Timer';
class App extends Component {
// ES6 class property/class field syntax allows you to remove constructor when just being used to initialize state
state = {
sessionDuration: 5, // TODO: change back to 1500 when testing done
breakDuration: 3, // TODO: change back to 300 when testing done
sessionTimeRemaining: 5, // TODO: change back to 1500 when testing done
breakTimeRemaining: 3, // TODO: change back to 300 when testing done
isSession: true,
timerOn: false,
sessionNumber: 0,
playAlarm: false
}
// Using property initializer syntax to avoid need to bind, since arrow functions don't create their own this context and use value of enclosing context instead. transform-class-properties Babel plugin necessary to use this syntax (included in Create React App). Refer to https://itnext.io/property-initializers-what-why-and-how-to-use-it-5615210474a3 for more details
// DURATION CHANGES
decreaseBreakDuration = () => {
// Conditional statement prevents decrease when break is at 1 minute
if (this.state.breakDuration === 60) {
return undefined;
} else {
this.setState({
breakDuration: this.state.breakDuration - 60
});
}
}
increaseBreakDuration = () => {
this.setState({
breakDuration: this.state.breakDuration + 60
});
}
decreaseSessionDuration = () => {
// Conditional statement prevents decrease when session is at 5 minutes
if (this.state.sessionDuration === 300) {
return undefined;
} else {
this.setState({
sessionDuration: this.state.sessionDuration - 60,
sessionTimeRemaining: this.state.sessionTimeRemaining - 60
});
}
}
increaseSessionDuration = () => {
this.setState({
sessionDuration: this.state.sessionDuration + 60,
sessionTimeRemaining: this.state.sessionTimeRemaining + 60
});
}
manageBreak = () => {
this.setState({
playAlarm: false
});
this.time = setInterval(() => {
this.setState({
breakTimeRemaining: this.state.breakTimeRemaining - 1
});
if (this.state.breakTimeRemaining === 0) {
this.handleBreakComplete();
}
}, 1000);
}
manageSession = () => {
this.setState({
playAlarm: false
});
// Every 1,000 ms (1 second), subtract 1 (a single second) from displayed sessionTimeRemaining. Assigned to this.time (scoped to entire class) in order to pass it to clearInterval() when pause button is clicked
this.time = setInterval(() => {
this.setState({
sessionTimeRemaining: this.state.sessionTimeRemaining - 1
});
if (this.state.sessionTimeRemaining === 0) {
this.handleSessionComplete();
}
}, 1000);
}
handleSessionComplete = () => {
clearInterval(this.time);
this.setState({
playAlarm: true,
sessionNumber: this.state.sessionNumber + 1
})
if (this.state.sessionNumber === 4) {
this.handlePomodoroCycleDone();
} else {
this.setState({
timerOn: false,
sessionTimeRemaining: this.state.sessionDuration,
breakTimeRemaining: this.state.breakDuration,
isSession: !this.state.isSession,
});
}
}
handlePomodoroCycleDone = () => {
// TODO: Display message in modal
console.log('Great work! You finished a pomodoro cycle (four sessions). Time to relax.')
// Change back to default values
this.setState({
isSession: true,
timerOn: false,
sessionDuration: 5, // TODO: change back to 1500
breakDuration: 3, // TODO: change back to 300 when testing done
sessionTimeRemaining: 5, // TODO: change back to 1500
});
}
handleBreakComplete = () => {
clearInterval(this.time);
this.setState({
timerOn: false,
sessionTimeRemaining: this.state.sessionDuration,
breakTimeRemaining: this.state.breakDuration,
isSession: !this.state.isSession,
playAlarm: true
});
}
// PLAY, PAUSE, RESTART BUTTONS
startTimer = () => {
this.setState({
timerOn: true,
});
if (this.state.isSession) {
this.manageSession();
} else {
this.manageBreak();
}
}
pauseTimer = () => {
// Stops setInterval's calling its (setState) callback every 1000 ms
clearInterval(this.time);
this.setState({
timerOn: false
});
}
resetTimer = () => {
// Stops setInterval's calling its (setState) callback every 1000 ms
// TODO: Display 4 unchecked circle icons again
clearInterval(this.time);
this.setState({
timerOn: false,
sessionDuration: 5, // TODO: change back to 1500
breakDuration: 3, // TODO: change back to 300 when testing done
sessionTimeRemaining: 5, // TODO: change back to 1500
breakTimeRemaining: 3, // TODO: change back to 300 when testing done
sessionNumber: 0
});
}
render() {
return (
<Timer
breakDuration={this.state.breakDuration}
sessionDuration={this.state.sessionDuration}
decreaseBreakDuration={this.decreaseBreakDuration}
increaseBreakDuration={this.increaseBreakDuration}
decreaseSessionDuration={this.decreaseSessionDuration}
increaseSessionDuration={this.increaseSessionDuration}
sessionTimeRemaining={this.state.sessionTimeRemaining}
breakTimeRemaining={this.state.breakTimeRemaining}
timerOn={this.state.timerOn}
sessionNumber={this.state.sessionNumber}
isSession={this.state.isSession}
startTimer={this.startTimer}
pauseTimer={this.pauseTimer}
resetTimer={this.resetTimer}
playAlarm={this.state.playAlarm}
/>
);
};
}
export default App;
Also here's Timer.js:
import Audio from './Audio';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlay } from '@fortawesome/free-solid-svg-icons';
import { faPause } from '@fortawesome/free-solid-svg-icons';
import { faUndo } from '@fortawesome/free-solid-svg-icons';
import React from 'react';
import PomodoroIcons from './PomodoroIcons';
import DurationControls from './DurationControls';
const TimeFormat = require('hh-mm-ss');
const Timer = props => (
<div className="timer">
<DurationControls
breakDuration={props.breakDuration}
sessionDuration={props.sessionDuration}
increaseBreakDuration={props.increaseBreakDuration}
decreaseBreakDuration={props.decreaseBreakDuration}
increaseSessionDuration={props.increaseSessionDuration}
decreaseSessionDuration={props.decreaseSessionDuration}
/>
{/* TIME REMAINING */}
<p className="time-remaining">
{props.isSession ? TimeFormat.fromS(props.sessionTimeRemaining) : TimeFormat.fromS(props.breakTimeRemaining)}
</p>
{/* PLAY, PAUSE, RESTART BUTTONS */}
<div className="bottom-btns">
<div className={props.timerOn ? 'hidden' : ''}>
<FontAwesomeIcon
role="button"
onClick={props.startTimer}
icon={faPlay}
className="btn bottom-btn"
size="4x"
/>
</div>
<div className={props.timerOn === false ? 'hidden' : ''}>
<FontAwesomeIcon
role="button"
onClick={props.pauseTimer}
icon={faPause}
className="btn bottom-btn"
size="4x"
/>
</div>
<FontAwesomeIcon
role="button"
onClick={props.resetTimer}
icon={faUndo}
className="btn bottom-btn"
size="4x"
/>
</div> {/* End bottom-btns */}
<PomodoroIcons sessionNumber={props.sessionNumber} />
{props.playAlarm ? <Audio /> : null}
</div>
);
export default Timer;
Upvotes: 0
Views: 221
Reputation: 8130
i dont follow everything going on here.. but at a glance this is an issue:
class Audio extends Component {
constructor() {
super();
this.url = "./chime.mp3";
this.audio = new Audio(this.url);
}
render() {
return (
this.audio
);
}
}
the call stack exceeded error is because you're entering into an infinite loop. You instantiate Audio
inside of Audio
which will make another Audio
object and so on into infinity.
Upvotes: 2