I want to show as an output:
User was online for 12 minutes
I'm calculating the timespent by the user on the page by setting a timeinterval.
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 60000);
}
render () {
<div> User online for {this.state.time} minutes</div>
}
But this gives me:
O/P:
User online for 1532887387453 minutes
Hence this conversion for converting epoch into human readable minutes.
Is this possible?
this time is in milliseconds. So, to get minutes, you should divide it by 1000 (to get seconds), and then by 60 (to get mniutes), or simply divide it by 60000. So, use code as the following:
constructor(props) {
super(props);
this.state = { time: 0 };
}
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: (Date.now() / 60000) - this.state.time }), 60000);
}
render() {
return (<div>User online for {this.state.time} minutes</div>);
}
If you're using setInterval to update the time spent, why can't you simply increment the counter after each minute,
constructor() {
super();
this.state = {
timeSpent: 0, // if you want to show 1 as the minimum, set as 1 here
}
}
componentDidMount() {
this.interval = setInterval(() => this.setState({ timeSpent: ++this.state.timeSpent }), 60000);
}
render () {
<div> User online for {this.state.timeSpent} minutes</div>
}
P.S: But keeping this time calculation logic inside a component is really bad approach.
Related
I'm trying to achieve the slider functionality, where I want to slider to move to the next question after every three seconds [this part is working fine]
Features i'm trying to achieve:
OnMouseEnter: Timer stops, css animation stops(working)
OnMouseleave: Start the timer from remaining time left
OnClick: Change Question number to the selected items (working) and start animation
The issue is when I changed the question_number by onClick event, the next question start showing after every 1 seconds instead of 3 seconds. I think the problem is somehow with the React Re-rendering.
setSlider is simple useState has object:
const [timeSlider, setTimeSlider] = useState({
question_length: 4,
question_number: 0,
current_time: 0
});
I'm using useEffect to check the viewport width so react Re-render the components everytime, Viewport changes
I tried to store the Interval for timer in a react useRef. but still not working. Here is my code:
I use useRef, so I can Resume, Pause and updateNumber outSide where the first time setInterval started
const interval = useRef(null);
After EverySecond update the useState: Seconds
useEffect(() => {
setRemaining({
start_time: Date.now(),
stop_time: 0
});
interval.current = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
pauseSlider();
return () => clearInterval(interval);
}, []);
And when ever the seconds state changed, I make sure after every three seconds. question_number increased only when its less than 3
useEffect(() => {
if (seconds === 3) {
setSeconds(seconds => 0);
setTimeSlider(prev => prev.question_number === 3 ? { ...timeSlider,
question_number: 0
} : { ...timeSlider,
question_number: timeSlider.question_number + 1
});
}
}, [seconds]);
Below are the pause, resume and update function.
function pauseSlider() {
setSliderHover(true)
setRemaining({
start_time: 0,
stop_time: Date.now()
});
clearInterval(interval.current);
}
function resumeSlider() {
setSliderHover(false)
setRemaining(prev => ({ ...prev,
start_time: prev.stop_time,
stop_time: 0
}));
interval.current = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
}
function updateNumber(num) {
setSliderHover(false)
setSeconds(0)
setRemaining({
start_time: 0,
stop_time: 0
});
clearInterval(interval.current);
interval.current = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
setTimeSlider({ ...timeSlider,
question_number: num
});
}
The Problem could be in these functions, I tried to use setTimeout instead of setInterval same results.
Below is the picture of slider, so you have more clear idea what i'm trying to achieve.
Instead of using the JS, I used the GSAP for this here is my solution for it
useLayoutEffect(() => {
const ctx = gsap.context((self) => {
TimelineRef.current = gsap.timeline({
defaults: {
repeat: -1,
ease: "none",
}
})
.to(".timerSlier_left",
{
x: "0%",
duration: 10,
onRepeat: () => UpdateToNextQuestion(timeSlider),
}
)
}, ContainerRef); // <- Scope!
return () => ctx.revert(); // <- Cleanup!
}, []);
const [ remaining, setRemaining ] = useState(0);
useEffect(()=>{
if ( isInViewport ){
resumeSlider()
}else{
pauseSlider()
}
}, [ isInViewport ]);
const UpdateToNextQuestion = () => {
settimeSlider( prev => prev.question_number === 3 ? ({...prev, question_number: 0}) : ({...prev, question_number: prev.question_number+1}))
}
function pauseSlider(){
console.log("stop slider")
setSliderHover(true)
TimelineRef.current.pause();
}
function resumeSlider(){
setSliderHover(false)
TimelineRef.current.resume();
}
function updateNumber(num){
TimelineRef.current.restart();
settimeSlider( timeSlider => ({ ...timeSlider, question_number: num }) );
console.log("RESUMING")
setSliderHover(false)
TimelineRef.current.resume();
}
I'm handing timeout functionality from GSAP timeline.
I'm trying to track the time for each todo in my app by saving it in an array of objects. I managed to make it work, but since I'm updating the state only when I stop the timer, the component is not displaying each second passing.
What would be the simplest way to update the state each second? I've tried for a few days (mainly with setInterval()) but I still can't make it work since it's a little beyond my knowledge.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
// todos array of objects
todos: null,
// value to hold temp text input
value: '',
};
...
}
// start&stop timer + updating counters
startTimer(todoKey) {
const newTodos = this.state.todos.map(element => {
if (element.key === todoKey) {
if (element.status === "stopped") {
return { ...element, status: 'started', startTime: new Date()};
} else {
let sessionSpent = new Date() - element.startTime;
let progress = element.goal !== null ? ((element.spent === null ? sessionSpent : element.spent + sessionSpent) * 1 / element.goal) : 1;
return { ...element, status: 'stopped', startTime: null, progress: progress, spent: element.spent === null ? sessionSpent : element.spent + sessionSpent };
}
} else { return element; }
});
this.setState({
todos: newTodos
});
}
....
After some more trials, I managed to make it work.
I will leave it here if anyone is having the same problem, but be aware: I'm just starting, so it's possible that I'm doing a surgery with a hammer.
If that's the case, please suggest a better solution. Thank you.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
//todos array of objects
todos: null,
//value to hold temp text input
value: '',
//timer status: need to save it outside of the setInterval() loop so it doesn't stop after 1 iteration.
timerStatus: false,
//setInterval function container
interval: null,
// active timer todoKey: saving it here so I can avoid that a timer is stopped by pressing another timer's button.
activeTimer: null
};
...
}
// start&stop timer + updating counters
startTimer(todoKey) {
// first I check that there's no active timer
if (!this.state.timerStatus) {
// if no timer is active, I save the active timer's key in the state(so I can forbid activities on the timer from another timer's button)
this.setState({ activeTimer: todoKey });
// here I start the setInterval() function in a variable and save it into a variable (so later I can save it in the state)
let interval = setInterval(() => {
// looping through the array
const newTodos = this.state.todos.map(element => {
// finding the right object in the array so I can save the progress and the time spent, each second
if (element.key === todoKey) {
let sessionSpent = new Date() - element.startTime;
let progress = element.goal !== null ? ((element.spent === null ? sessionSpent : element.spent + sessionSpent) * 1 / element.goal) : 1;
return { ...element, status: 'started', startTime: new Date(), spent: element.spent + 1000, progress: progress }
} else {
return element;
}
});
this.setState({
todos: newTodos
});
}, 1000);
// setting the timerStatus as true(so I can have a trouth source outside of the loop when I need to stop it)
// saving the variable containint setInterval() in the state (so I can use clearInterval() from the else block
this.setState({ timerStatus: true, interval: interval });
console.log(this.state.timerStatus);
} else {
// checking if the pressed button is the one belonging to the active timer
if (todoKey === this.state.activeTimer) {
// saving in the state the timer's status (thus making possible for another timer to start
// clearing the setInterval() function
this.setState({ timerStatus: false, interval: clearInterval(this.state.interval) });
// looping through the array to save the current progress
const newTodos = this.state.todos.map(element => {
if (element.key === todoKey) {
let sessionSpent = new Date() - element.startTime;
let progress = element.goal !== null ? ((element.spent === null ? sessionSpent : element.spent + sessionSpent) * 1 / element.goal) : 1;
return { ...element, status: 'stopped', startTime: null, progress: progress, spent: element.spent === null ? sessionSpent : element.spent + sessionSpent };
} else {
return element;
}
}
);
// updating the state
this.setState({
todos: newTodos
});
} else {
// showing an error Toast when user tries to start another timer if there's one already active (when the check (todoKey === this.state.activeTimer) is false)
Toast.show("A timer is already running and multitasking is bad D:", Toast.LONG);
}
}
}
Here is a simple example:
this.setInterval( () => {
let d = new Date();
let result = d.getHours() + d.getMinutes() / MINUTES_IN_HOUR;
this.setState({
timeLineTop: result
})
}, 1000);
Please help me it always says Cannot read property 'commit' of undefined. Here is my code in store.js.
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state:{
timer: null,
totaltime: (25 * 60)
},
mutations:{
startTimer: (state, context) => {
//here is the problem part I think
state.timer = setInterval(() => context.commit('countDown'),
1000)
},
countDown: state => {
var time = state.totaltime
if(time >= 1){
time--
}else{
time = 0
}
},
stopTimer: state => {
clearInterval(state.timer)
state.timer = null
},
resetTimer: state => {
state.totaltime = (25 * 60)
clearInterval(state.timer)
}
},
getters:{
minutes: state => {
const minutes = Math.floor(state.totaltime / 60);
return minutes;
},
seconds: (state, getters) => {
const seconds = state.totaltime - (getters.minutes * 60);
return seconds;
}
},
actions:{
}
})
I have problem it debugging. it always says like this
'Cannot read property 'commit' of undefined'
Here is my Timer.vue code for calling
methods: {
formTime(time){
return (time < 10 ? '0' : '') + time;
},
startTimer(){
this.resetButton = true
this.$store.commit('startTimer')
},
stopTimer(){
this.$store.commit('stopTimer')
},
resetTimer(){
this.$store.commit('resetTimer')
},
},
computed: {
minutes(){
var minutes = this.$store.getters.minutes;
return this.formTime(minutes)
},
seconds(){
var seconds = this.$store.getters.seconds;
return this.formTime(seconds);
},
timer: {
get(){
return this.$store.state.timer
}
}
}
My code in Timer.vue script computed and methods. I cannot track where the problem is... Please help me Im stuck with this here.
Mutations do not have access to any context. They are meant to be atomic, that is they work directly with one facet of state. You should make your startTimer an action that commits the timer and then starts the countdown
mutations: {
// add this one for setting the timer
setTimer (state, timer) {
state.timer = timer
}
},
actions: {
startTimer ({ commit }) {
commit('stopTimer') // just a guess but you might need this
commit('setTimer', setInterval(() => {
commit('countDown')
}, 1000))
}
}
This would need to be called via dispatch instead of commit
this.$store.dispatch('startTimer')
I have a table that renders a mapped patientInfo array. When the patient.status is not empty/has value, a countdown timer starts counting down for individual patient in the array. This happens on componentDidMount(). Now I want to clearInterval on componentWillUnMount() but the countdown timer doesn’t stop.
basically all I need is the countdown to clear when the time get to 0. Lets say the timer starts counting down from 60 secs when it gets to 0, clear interval. there is no stop button or anything like that to execute. I need it clear interval automatically when time gets to 0 sec. the countdown time start for individual patient when they have value in status. hope that makes sense
PatientInfo Array
patientInfo = [
{ count: 959999, room: "1", name: 'John Nero', status: ''},
{ count: 959999, room: "2", name: 'Shawn Michael', status: ''},
{ count: 959999, room: "3", name: 'Gereth Macneil', status: ''}
]
//starts countdown when the patient.status has value which comes from user input
componentDidMount() {
this.countDownInterval = setInterval(() => {
this.setState(prevState => ({
patientInfo: prevState.patientInfo.map((patient) => {
if (patient.status !== '') {
// subtract a sec
return { ...patient, count: patient.count - 1000};
}
return patient;
})
}));
}, 1000);
}
//when the patient.count is 950999 clearInterval doesn't work
//edited after some comments but still doesn't work
componentWillUnmount() {
this.state.patientInfo.map((patient) => {
if (patient.count <= 950999) {
clearInterval(this.countDownInterval);
}
});
}
//after a few try the following seems to work but not sure if this is the correct way
componentDidMount() {
this.countDownInterval = setInterval(() => {
this.setState(prevState => ({
patientInfo: prevState.patientInfo.map((patient) => {
if (patient.status !== '') {
if (patient.count <= 950999) {
clearInterval(this.countDownInterval);
}
return { ...patient, count: patient.count - 1000 };
}
return patient;
})
}));
}, 1000);
}
I think the problem here is that you are clearing the interval from a call to setState from componentWillUnmount. If the component is going to unmount there is not more state to set. The component won't be there.
From the docs,
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount().
And
You should not call setState() in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again.
At max you can do this,
componentWillUnmount() {
clearInterval(this.countDownInterval);
}
I'm passing arguments into my render() function the following way:
render() {
const { minerid, ownerid, logs, address } = this.props.miner
}
They are structured like so:
Sometimes, the script that is sending these logs crashes.
I have tried making this handler in order to add logs full of zeroes to the log array.
handleCrash = (logs) => {
let time = parseFloat(logs[logs.length - 1].time)
const newTime = Date.now()
const num = logs[logs.length - 1].gpus.num
if ((newTime - time) > 300) {
time = time+300
logs.push(
{
gpus: {
fans: Array(num).fill('0'),
hashrate: Array(num).fill('0'),
temps: Array(num).fill('0'),
num: num
},
miner: logs[logs.length - 1].miner,
time: time
}
)
}
}
I have then tried putting this in my componentDidMount() :
componentDidMount () {
this.handleCrash(this.props.miner.logs)
}
as well as putting handleCrash() in my render method like this:
render() {
this.handleCrash(this.props.miner.logs)
const { minerid, ownerid, logs, address } = this.props.miner
I have different functions manipulating this data calculating averages and graphing charts further down into the app.
They all work fine until I try to add these zero logs and I get "Error: <path> attribute d: Expected number, "MNaN,85.926498958…" and the graphs don't render anymore.
The zero logs look like they are the exact same structure as all the other logs.
Anyobody knows what am I doing wrong here? Thank you in advance!
Maybe something like this:
handleCrash = (logs) => {
let time = parseFloat(logs[logs.length - 1].time)
const newTime = Date.now()
const num = logs[logs.length - 1].gpus.num
if ((newTime - time) > 300) {
time = time+300
logs.push(
{
gpus: {
fans: Array(num).fill('0'),
hashrate: Array(num).fill('0'),
temps: Array(num).fill('0'),
num: num
},
miner: logs[logs.length - 1].miner,
time: time
}
)
}
return logs
}
render() {
const { minerid, ownerid, logs, address } = this.handleCrash(this.props.miner.logs)