I currently have a timer that works with a class but I need to use only functions so it works with the App.js file I am running to show what I need. Here is an example of how I would call my file from the App:
return (
<div>
<h1>Example Timer</h1>
<h2>Other files have been removed for example purposes</h2>
<Timer onClick ={this.startTimer} />
</div>
)
And here is my Timer class which needs to be converted to functions so I can export it like:
export default function Timer(onClick)
Here is the entire class component for the timer
import React from 'react';
import "./timer.css";
class Timer extends React.Component{
constructor(){
super();
this.state = { time: {}, seconds: 5, color: 'darkgrey'};
this.timer = 0;
this.startTimer = this.startTimer.bind(this);
this.startCountDown = this.startCountDown.bind(this);
}
convertToSeconds(sec){
let divisor_for_minutes = sec % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = {"m": minutes, "s": seconds};
return obj;
}
componentDidMount() {
let timeLeftVar = this.convertToSeconds(this.state.seconds);
this.setState({ time: timeLeftVar });
}
startTimer(){
// If timer is not set, set the interval
if(this.timer === 0 && this.state.seconds > 0){
// 1000 ms = 1 second
this.timer = setInterval(this.startCountDown, 1000);
}
}
startCountDown() {
let seconds = this.state.seconds - 1;
if(seconds >= 0){
if(seconds<=2){
this.setState({color: 'red'})
}
this.setState({
time: this.convertToSeconds(seconds),
seconds: seconds
});
}
else{
this.setState({time: this.convertToSeconds(5), seconds: 5, color: 'darkgrey'});
clearInterval(this.timer);
this.timer = 0;
}
}
render(){
return(
<div className="component-timer">
<div class="startbtn">
<button onClick={this.startTimer}>Start</button>
</div>
<div class="timer"><div style = {{color: this.state.color}}>
m: {this.state.time.m} s: {this.state.time.s}
</div>
</div>
</div>
);
}
}
export default Timer;
With a new version of react we can use Hook useState and it will look like this
import React, { useState } from 'react';
import "./timer.css";
const convertToSeconds = (sec) => {
let divisor_for_minutes = sec % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = {"m": minutes, "s": seconds};
return obj;
}
const Timer= () => {
const [seconds, setSeconds] = useState(5)
const [time, setTime] = useState(convertToSeconds(seconds))
const [color, setColor] = useState('darkgrey')
const [timer, setTimer] = useState(0)
const startTimer = () => {
// If timer is not set, set the interval
if(timer === 0 && seconds > 0){
// 1000 ms = 1 second
setTimer(setInterval(startCountDown, 1000))
}
}
const startCountDown = () => {
let newSeconds = seconds - 1;
if(newSeconds >= 0){
if(newSeconds<=2){
setColor('red')
}
setTime(convertToSeconds(newSeconds))
setSeconds(newSeconds)
}
else{
setTime(convertToSeconds(5))
setSeconds(5)
setColor('darkgrey')
setTimer(0)
}
}
return(
<div className="component-timer">
<div class="startbtn">
<button onClick={startTimer}>Start</button>
</div>
<div class="timer"><div style = {{color}}>
m: {time.m} s: {time.s}
</div>
</div>
</div>
)
}
export default Timer
This is the line by line conversion of the class component to function component using react hooks... (You need react version higher than 16.8.x https://reactjs.org/docs/hooks-intro.html)
import React, { useState, useEffect } from 'react';
import './timer.css';
export default function Timer() {
const [seconds, setSeconds] = useState(5);
const [time, setTime] = useState({});
const [color, setColor] = useState('darkgrey');
const [timer, setTimer] = useState(0);
//only executes when component mounts by passing "[]".
useEffect(() => {
setTime(convertToSeconds(seconds));
}, []);
function convertToSeconds(sec) {
let divisor_for_minutes = sec % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
let divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
var obj = { m: minutes, s: seconds };
return obj;
}
function startTimer() {
// If timer is not set, set the interval
if (timer === 0 && seconds > 0) {
// 1000 ms = 1 second
setTimer(setInterval(startCountDown, 1000));
}
}
function startCountDown() {
let _seconds = seconds - 1;
if (_seconds >= 0) {
if (_seconds <= 2) {
setColor('red');
}
setTime(convertToSeconds(_seconds));
setSeconds(_seconds);
} else {
setTime(convertToSeconds(5));
setSeconds(5);
setColor('darkgrey');
clearInterval(timer);
setTimer(0);
}
}
return (
<div className="component-timer">
<div className="startbtn">
<button onClick={startTimer}>Start</button>
</div>
<div className="timer">
<div style={{ color: color }}>
m: {time.m} s: {time.s}
</div>
</div>
</div>
);
}
You do not need function to work, you can use your class component and use it to render in directly in index.js or anywhere you want to render
Code is somewhat like this
import React from 'react';
import ReactDOM from 'react-dom';
import Timer from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<div>
<h1>Example Timer</h1>
<h2>Other files have been removed for example purposes</h2>
<Timer />
</div>
, document.getElementById('root'));
registerServiceWorker();
Related
I'm new to React and trying to add some audio to a tenzies game.
The audio is getting weirder every time I click the roll button. And after clicking for a while, the sound is fading away. There is also a warning in the console: 'The AudioContext was not allowed to start.'
I can't find out what causing this issue. Please help!
I don't know how to run react code in StackOverflow. So I'm adding a link to the github repository and live site URL of this game.
Here is the full App.js component's code:
import React from 'react';
import Die from './Components/Die';
import { nanoid } from 'nanoid';
import Confetti from 'react-confetti';
import {Howl} from 'howler';
import winSound from './audio/win.mp3';
import rollSound from './audio/roll.mp3';
import holdSound from './audio/hold.mp3';
export default function App(){
const [dice, setDice] = React.useState(generateNewDice());
const [tenzies, setTenzies] = React.useState(false);
const [audio, setAudio] = React.useState(true);
const [rollCount, setRollCount] = React.useState(0);
const [timer, setTimer] = React.useState(0);
const [timerRunning, setTimerRunning] = React.useState(false);
function holdDieObject(){
return {
value: Math.floor(Math.random()*6) + 1,
isHeld: false,
id: nanoid()
}
}
function generateNewDice(){
let newDice= [];
for(let i=0; i<10; i++){
newDice.push(holdDieObject());
}
return newDice;
}
React.useEffect(()=>{ //Count time per 10 milliseconds when timer is running
let interval;
if(timerRunning){
interval = setInterval(() => {
setTimer((prevTime) => prevTime + 10)
}, 10)
}else{
clearInterval(interval);
}
return () => clearInterval(interval);
},[timerRunning])
React.useEffect(()=>{ //Check all the dice are matched or not
const someDiceHeld = dice.some(die => die.isHeld);
const allDiceHeld = dice.every(die => die.isHeld);
const firstDiceValue = dice[0].value;
const allSameValue = dice.every(die=> die.value === firstDiceValue);
if(someDiceHeld){
setTimerRunning(true);
}
if(allDiceHeld && allSameValue){
setTenzies(true);
// audio && victorySound.play(); // This brings up dependency warning. So moved it to the bottom
setTimerRunning(false)
}
},[dice])
const victorySound = new Howl({
src: [winSound]
})
if(tenzies){
audio && victorySound.play(); // Here
}
const rollDieSound = new Howl({
src: [rollSound]
})
const holdDieSound = new Howl({
src: [holdSound]
})
function holdDice(id){
audio && holdDieSound.play();
setDice(oldDice => oldDice.map(die =>{
return die.id===id ?
{
...die,
isHeld: !die.isHeld
} :
die;
}))
}
function rollDice(){
if(!tenzies){
setDice(oldDice => oldDice.map(die=>{
audio && rollDieSound.play();
return die.isHeld ? die : holdDieObject();
}))
setRollCount(prevCount => prevCount + 1);
}else{
setTenzies(false);
setDice(generateNewDice());
setRollCount(0);
setTimer(0);
}
}
function toggleMute(){
setAudio(prevState => !prevState);
}
function startNewGame(){
setTenzies(false);
setDice(generateNewDice());
}
const minutes = <span>{("0" + Math.floor((timer / 60000) % 60)).slice(-2)}</span>
const seconds = <span>{("0" + Math.floor((timer / 1000) % 60)).slice(-2)}</span>
const milliseconds = <span>{("0" + ((timer / 10) % 100)).slice(-2)}</span>
const dieElements = dice.map((die) => {
return <Die key={die.id}
value={die.value}
isHeld={die.isHeld}
holdDice={()=> holdDice(die.id)}
/>
})
return(
<div>
<main className="board">
<button onClick={toggleMute} className="mute-btn">{audio ? "🔉" : "🔇"}</button>
<h1>Tenzies</h1>
<p>Roll untill the dice are the same. Click each die to freeze it at its current value between rolls.</p>
<div className="die-container">
{dieElements}
</div>
<button onClick={rollDice}>Roll</button>
{tenzies && <div className="scoreboard">
<h2>Congratulations!</h2>
<p className='rollCount'>Rolled: {rollCount}</p>
<p className="rolltime">Time Taken: {minutes}:{seconds}:{milliseconds}</p>
<h3>Your Score: 4500</h3>
<button className='close' onClick={startNewGame}>New Game</button>
</div>}
</main>
{tenzies && <Confetti className="confetti" recycle={false} />}
</div>
)
}
This is normal timer in React Native
There is something wrong with countDown function, I've been trying to figure it out for hours already.
I check line by line useEffect function is right, convertingTotalTime also works fine.
There must be something wrong with setState functions, as they are not syncronus, but CountDown function is called every second, you can try code snippet ...
can you please help me understand what's wrong with code?
import React, { useEffect, useState } from "https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js";
import { Button, StyleSheet, Text, View } from "https://cdnjs.cloudflare.com/ajax/libs/react-native-web/0.17.1/index.js";
export default function App() {
const [totalTime, setTotalTime] = useState(50000);
const [playMode, setPlayMode] = useState(false);
const [progress, setProgress] = useState(100);
const [timeString, setTimeString] = useState(`00 : 00`);
const [second, setSec] = useState(0);
const [minute, setMin] = useState(0);
useEffect(() => {
if (playMode && totalTime > 0) {
let myInterval = setInterval(() => {
countDown();
console.log("countdown");
return () => {
clearInterval(myInterval);
};
}, 1000);
}
}, [playMode]);
const countDown = () => {
// console.table(hour, minute, second);
let [sec, min] = [second, minute];
let _totalTime = totalTime;
_totalTime = _totalTime - 1000;
sec = sec - 1;
if (sec == 0 && min >= 1) {
min = min - 1;
}
if (min == 0 && sec == 0) {
// coundown finished
console.warn("counter must stop");
}
setSec((prevState) => sec);
setMin((prevState) => min);
setTotalTime((prevState) => _totalTime);
setTimeString(
`${min < 10 ? "0" + min : min} : ${sec < 10 ? "0" + sec : sec} `
);
// this function is for later use => circular progress bar
setProgress((prevProgress) => {
if (prevProgress == 0) {
return 100;
}
let x = 100 / _totalTime;
return progress - x;
});
};
const convertTotalTime = () => {
let timeToConvert = totalTime;
let min = +Math.floor(timeToConvert / 60 / 1000);
timeToConvert -= Math.floor(timeToConvert / 1000);
setMin((prevState) => prevState + min);
let sec = +Math.floor(timeToConvert / 1000);
timeToConvert -= Math.floor(timeToConvert);
setSec((prevState) => prevState + sec);
};
const startTimerHandler = () => {
convertTotalTime();
setPlayMode(true);
};
return (
<View style={styles.container}>
<Text>{timeString}</Text>
<Button onPress={startTimerHandler} title="Start" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-native-web/0.17.1/index.js"></script>
Your import seems to be wrong...
import React, { useEffect, useState } from "react";
import { Button, StyleSheet, Text, View } from "react-native";
Cheers, I hope you solved your problem!
So the answer to this is following
In class based Components we usually destructure the state, update value and setIt again.
See Code Below
```
export default function App() {
const [second, setSec] = useState(0);
const [minute, setMin] = useState(0);
...
const countDown = () => {
// state destructure
let [sec, min] = [second, minute];
// update
sec = sec - 1;
// set it again
setSec(sec)
// the problem is console.log(second == sec) FALSE
// here state wont update for no reason....
// the following is kind of fix
setSec(prevState => prevState -1)
// state updates now BUT console.log(second == sec) FALSE !
// code below won't ever work
if (minute == 0 && second == 0) {
// coundown finished
console.warn("counter must stop");
}
}
```
So here is the only solution I found ...
```
export default function App() {
const [second, setSec] = useState(0);
const [minute, setMin] = useState(0);
...
const countDown = () => {
let [sec, min] = [second, minute];
// keep track of both state and local variable ...
sec = sec - 1;
setSec((prevState) => prevState - 1);
if (sec == 0 && min >= 1) {
min = min - 1;
setMin((prevState) => prevState - 1);
}
if (min == 0 && sec == 0) {
// coundown finished
console.warn("counter must stop");
}
...
};
```
I am trying to make a countdown timer using React hooks. The seconds part of the timer is working as expected, but I am encountering an issue when updating the minute part. In the below example, I want the timer to start from 05:00 and then on click of a button update to 04:59, 04:58 and so on. But when I click the button, instead of giving 04:59, it gives me 03:59. Attaching the code for the same below. Please let me know where I am getting it wrong.
import React, { useState } from "react";
const padWithZero = num => {
const numStr = num.toString();
return numStr.length === 1 ? "0" + numStr : numStr;
};
const Clock = () => {
let timer;
const [mins, setMins] = useState(5);
const [secs, setSecs] = useState(0);
const startHandler = () => {
timer = setInterval(() => {
setSecs(prevSecs => {
if (prevSecs === 0) {
setMins(prevMins => prevMins - 1);
return 59;
} else return prevSecs - 1;
});
}, 1000);
};
return (
<div>
<h1>{`${padWithZero(mins)}:${padWithZero(secs)}`}</h1>
<button onClick={startHandler}>Start</button>
</div>
);
};
export default Clock;
I don't know the exact reason why it decreases your minutes two times from 5 to 4 and from 4 to 3.
But I modified your code and added resetting feature - each click to button will resets timer back to initial 5:00. And now it works correctly:
import React, { useState } from "react";
const padWithZero = num => {
const numStr = num.toString();
return numStr.length === 1 ? "0" + numStr : numStr;
};
const INIT_SECS = 0;
const INIT_MINS = 5;
const Clock = () => {
let timer;
const [mins, setMins] = useState(INIT_MINS);
const [secs, setSecs] = useState(INIT_SECS);
const [storedTimer, setStoredTimer] = useState(null);
const startHandler = () => {
if (storedTimer) {
clearInterval(storedTimer);
setMins(INIT_MINS);
setSecs(INIT_SECS);
}
const newTimer = setInterval(() => {
setSecs(prevSecs => {
if (prevSecs === 0) {
setMins(prevMins => prevMins - 1);
return 59;
} else return prevSecs - 1;
});
}, 1000);
setStoredTimer(newTimer);
};
return (
<div>
<h1>{`${padWithZero(mins)}:${padWithZero(secs)}`}</h1>
<button onClick={startHandler}>Start</button>
</div>
);
};
export default Clock;
I rewrote my class component to a functional component but I this I made a mistake somewhere. I can find out what it is. The timer keeps going when the time is up. I think its the useEffect that i use wrong? The class componed worked fine but I need the functional to use redux.
import React, {useEffect, useState} from 'react';
import '../style/App.scss';
function CountdownTimer(){
const io = require('socket.io-client');
const socket = io.connect("http://localhost:3001/", {
reconnection: false
});
useEffect(() => {
if(running){
handleStart()
}
});
let timer = null;
const [time, setTime] = useState(0.1*60);
const [running, setRunning] = useState(true);
const handleStart= () => {
if (running){
timer = setInterval(() => {
if (time === 0){
console.log("no more time");
handleStop()
}else{
const newTime = time - 1;
setTime(
newTime >= 0 ? newTime : handleStop
);
}
}, 1000);
}
}
const handleStop = () => {
if(timer) {
clearInterval(timer);
setRunning(false);
}
}
const format = (time) => {
const date = new Date(time * 1000);
let hh = date.getUTCHours();
let mm = date.getUTCMinutes();
let ss = date.getSeconds();
if (hh < 10) {hh = "0"+hh;}
if (mm < 10) {mm = "0"+mm;}
if (ss < 10) {ss = "0"+ss;}
return '00' !== hh ? hh+":"+mm+":"+ss : mm+":"+ss;
}
return(
<div className="icon-value">
<h1>{format(time)}</h1>
</div>
);
}
export default CountdownTimer;
I am attempting to make custom controls for an HTML5 video player, using the reat-html5video HoC example:
import videoConnect from 'react-html5video';
const MyVideoPlayer = ({ video, videoEl, children, ...restProps }) => (
<div>
<video {...restProps}>
{ children }
</video>
<p>
Here are the video properties for the above HTML5 video:
{ JSON.stringify(video) }
</p>
<a href="#" onClick={(e) => {
e.preventDefault();
// You can do what you like with the HTMLMediaElement DOM element also.
videoEl.pause();
}}>
Pause video
</a>
</div>
);
export default videoConnect(MyVideoPlayer)
It seems fairly simple to add functionality to a button via "onClick", as illustrated below:
<button className={styles.rewindBTN} onClick ={() => {
videoEl.currentTime -= 5;
}
} hidden={false} ></button>
<button id="playPauseBTN" className={styles.playPauseBTN} onClick={() => {
if (videoEl.paused) {
videoEl.play()
document.getElementById('playPauseBTN').style.backgroundImage = "url('./components/HoC/img/pause.png')";
} else {
videoEl.pause()
document.getElementById('playPauseBTN').style.backgroundImage = "url('./components/HoC/img/play.png')";
}
}
} hidden={false}></button>
<button className={styles.replayBTN} onClick ={() => {
videoEl.currentTime = 0;
videoEl.play()
}
} hidden={true} ></button>
I am able to access the props (i.e. 'currentTime' and 'duration') like this:
const updateTime = ( videoEl, currentTime ) => {
let curMins = Math.floor(videoEl.currentTime / 60);
let curSecs = Math.floor(videoEl.currentTime - curMins * 60);
let durMins = Math.floor(videoEl.duration / 60);
let durSecs = Math.floor(videoEl.duration - durMins * 60);
if(curSecs < 10){ curSecs = "0"+curSecs; }
if(durSecs < 10){ durSecs = "0"+durSecs; }
if(curMins < 10){ curMins = curMins; }
if(durMins < 10){ durMins = durMins; }
document.getElementById('curTimeText').innerHTML = curMins+":"+curSecs;
document.getElementById('durTimeText').innerHTML = durMins+":"+durSecs;
}
but I can't figure out how to fire the "updateTime()" function when the component mounts and update the time as the video is playing. The function does return currentTime/duration...I placed a call to the function inside the "playPauseBTN onClick" event and the info updates each time I click the playPauseBTN.
How do I get the function to display duration as soon as the component mounts and update the currentTime as the video plays?
I am new to the concept of Higher Order Components, so forgive me if this is an obvious blunder on my part.
Your feedback is much appreciated.
* UPDATED CODE * (wrapped the Video01Player component in a class)
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import PropTypes from 'prop-types';
import { Motion, spring } from 'react-motion';
import styled from 'styled-components';
import videoConnect from 'react-html5video';
import styles from '../html5Video.css';
import customNav from '../customNav.scss';
import Video01Labels from '../Video01Labels';
import Video01Captions from '../Video01Captions';
const overlayContainer = document.querySelector('.overlayContainer');
const progressBarFill = document.querySelector('.progressBarFill');
const playPauseBTN = document.querySelector('.playPauseBTN');
const curTimeText = document.querySelector(".curTimeText");
const durTimeText = document.querySelector(".durTimeText");
const videoEl = document.getElementById('vid01');
const src = "./components/HoC/video/video01.mp4";
class Video01Player extends Component {
constructor(props) {
super(props);
this.updateTime = this.updateTime.bind(this);
this.updateProgress = this.updateProgress.bind(this);
}
componentDidMount() {
this.updateTime(Video01Player, videoEl)
this.updateProgress(Video01Player, videoEl)
}
componentWillUnmount() {
}
componentWillReceiveProps() {
this.updateTime(Video01Player, videoEl)
this.updateProgress(Video01Player, videoEl)
}
// Update Video Time
updateProgress( Video01Player, videoEl, currentTime, duration ) {
// Calculate current progress
var value = (100 / videoEl.duration) * videoEl.currentTime;
// Update the slider value
document.getElementById('progressBarFill').style.width = value + '%';
}
updateTime( Video01Player, videoEl, currentTime, duration ) {
let curMins = Math.floor(videoEl.currentTime / 60);
let curSecs = Math.floor(videoEl.currentTime - curMins * 60);
let durMins = Math.floor(videoEl.duration / 60);
let durSecs = Math.floor(videoEl.duration - durMins * 60);
if(curSecs < 10){ curSecs = "0"+curSecs; }
if(durSecs < 10){ durSecs = "0"+durSecs; }
if(curMins < 10){ curMins = curMins; }
if(durMins < 10){ durMins = durMins; }
document.getElementById('curTimeText').innerHTML = curMins+":"+curSecs;
document.getElementById('durTimeText').innerHTML = durMins+":"+durSecs;
}
render() {
const { Video01Player, videoEl, children, paused, duration, currentTime, ...restProps } = this.props
return (
<div className={styles.videoContainer}>
<video className={styles.videoWrapper} id="vid01" src={src} type="video/mp4" { ...restProps } >
{ children }
</video>
<div className={styles.videoControlsWrapper}>
<button className={styles.rewindBTN} onClick ={() => {
videoEl.currentTime -= 5;
}
} hidden={false} ></button>
<button id="playPauseBTN" className={styles.playPauseBTN} onClick={( currentTime, duration ) => {
if (videoEl.paused) {
videoEl.play()
document.getElementById('playPauseBTN').style.backgroundImage = "url('./components/HoC/img/pause.png')";
} else {
videoEl.pause()
document.getElementById('playPauseBTN').style.backgroundImage = "url('./components/HoC/img/play.png')";
}
}
} hidden={false}></button>
<button className={styles.replayBTN} onClick ={() => {
videoEl.currentTime = 0;
videoEl.play()
}
} hidden={true} ></button>
<div className={styles.progressBar}>
<div id="progressBarFill" className={styles.progressBarFill}></div>
</div>
<button className={styles.forwardBTN} onClick={() => {
videoEl.currentTime += 5;
}
}></button>
<div className={styles.timeWrapper}>
<span id="curTimeText" className={styles.curTimeText}>00:00</span> / <span id="durTimeText" className={styles.durTimeText}>00:00</span>
</div>
</div>
<div className={styles.overlayContainer} data-tid="container">
<Video01Labels />
<Video01Captions />
</div>
</div>
);
}
}
export default videoConnect(Video01Player);