reactjs / react-timer-mixin

TimerMixin provides timer functions for executing code in the future that are safely cleaned up when the component unmounts

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[ES6] Provide Example

rileybracken opened this issue · comments

To be more consistent with the React Native docs is it possible to provide a ES6 Class syntax of this in the documentation?

https://facebook.github.io/react-native/docs/timers.html#timermixin

My understanding is ES6 dropped support for Mixins and it has not been implemented yet. As a workaround for my own RN projects I have included reactMixin to get this to work. See code sample below.

Keen to know if this is 'recommended' or not though.

'use strict';

var React = require('react-native');
var TimerMixin = require('react-timer-mixin');
var reactMixin = require('react-mixin');

class MyClass extends Component {

  constructor(props) {
    super(props);
  }

    componentDidMount() {
      this.setTimeout(
        () => { console.log('tick'); },
        5000
      );
    }
}

reactMixin(MyClass.prototype, TimerMixin);

module.exports = MyClass;

Doesn't seem to work. Getting this.setTimeout is not a function.

I've used it like this for ES6:

import TimerMixin from 'react-timer-mixin';

//inside your component:
componentDidMount () {
    TimerMixin.setTimeout(
      () => { console.log('I do not leak!'); },
      5000
    );
  }

And it works fine.

@sandagolcea That won't unmount when your component unmounts.

@olliebrennan I am also following your advice. Thank you for sharing.
"Reflux" is not used anywhere in your code.

ES6 dropped mixins for a reason; they're bad. (Source 1, Source 2)

@spicyj - using this will:

componentDidMount() {
  this.timer = TimerMixin.setTimeout(() => {
    console.log('I do not leak!');
  }, 5000);
}

componentWillUnmount() {
  TimerMixin.clearTimeout(this.timer);
}

@richarddewit As will this, without TimerMixin at all:

componentDidMount() {
  this.timer = setTimeout(() => {
    console.log('I do not leak!');
  }, 5000);
}

componentWillUnmount() {
  clearTimeout(this.timer);
}

@spicyj Oh of course, haha derp :P then why use the mixin at all?

Yah all this really helped me. I agree with you @richarddewit

Hey, do any of you guys know how to do this with setInterval instead of setTimeout? If so, that would be useful within the app I am using. I tried switching out anything with setTimeout with setInterval but it didn't work.

@egoldiez : see here: https://github.com/reactjs/react-timer-mixin/blob/master/TimerMixin.js#L57-L76

setInterval <> clearInterval
setTimeout <> clearTimeout
setImmediate <> clearImmediate
requestAnimationFrame <> cancelAnimationFrame

@richarddewit : the point of this mixin is that when you use the this.setInterval/setTimeout/setImmediate/requestAnimationFrame methods, it will track the handle and release them at willUnmount time so you never have to think about releasing the things anymore.

When you use ES6 class, mixin won't work out of the box but you might still be able to do:

class C extends Component {
  componentWillUnmount () {
    TimerMixin.componentWillUnmount.call(this);
  }

  foo () {
    TimerMixin.setTimeout.call(this, () => { ... }, 1000);
  }
}

but I prefer to just call the relevant function and release them in componentWillUnmount at shown by @spicyj .

@gre +1 for your approach where you piggy back off of the Mixin's automatic registration of all timers and full flush of them come time to unmount -- reducing pilot error when forgetting to clear out more than one timer one by one, in a method that can live somewhere else in the file.

yea, so it must be bound to the component or you might as well just use regular timers. This is a small lib, all we gotta do is make it so you can wrap your component like this:

C extends Timer(Component) {
...

I'm pretty sure that's the interface we're supposed to be promoting for this sorta basic wrapper that doesn't need to pass props. if im wrong, what is it? it will take 2 seconds to provide this extra interface to the code.

@faceyspacey looking at your fork (faceyspacey@fed5928), great! I hope something like this can get merged.

Would it be preferred to avoid having to make super() call in componentWillUnmount? I guess that would involve "hijacking" componentWillUnmount and calling the original one (if it exists)... maybe that is bad practice...?

If you look how @D1no did it in another package I've worked on:

https://github.com/ultimatejs/tracker-react/blob/master/main.js

you will hear some of the slack received in this React github issue:

facebook/react#6162

Judging by how the React team decided to require you to automatically do things like bind component methods when using ES6 classes, I feel like the "react way" is all about doing things the built-in way, even if it means more boilerplate. I'm still on the fence myself, but I am interested in doing things the best practices way react developers _expect_--so if they expect to call super when using such tools, fine. If this ends up being an exception--which it very well might be--well then I'd probably lean toward automating it like @D1no did so you don't have to call super.

I see, thanks for explaining! Do you plan to PR this?

yea. basically when we decide exactly which way to do. my current way was super duper quick by the way. i gotta find a second to see if there is a better way to implement the way that requires super.

but basically, when the group decides which is the best way to go, we can get that PR going really quickly and then hopefully merged.

I'm thinking now, having to call super is completely the wrong way to go for this. The whole point of this package is it automatically cleans up timers. You're back to square one when you start using componentWillUnmount and have to remember to call super. So I propose we do what @D1no did with http://github.com/ultimatejs/tracker-react

It's trivial to implement, copying and pasting that code. So it's just a matter of getting it merged. I'm in the middle of a project where I'll need to insure timers are fault-proof soon--when I do I'll get back to this and link you guys the fork to use, and of course make the PR.

RN19 w/ Redux -- I've tried all the ES6 implementations here and all of them seem to leak in some way. In the case of calling Redux actions in a setInterval if I open an other instance of the component (after unmounting the other) I see that the action is actually getting called twice.

can you paste the implementation you're using that triggers it being called twice. That sounds like a problem where setInterval is being called on the prototype rather than an instance.

componentDidMount() {
  this.timer =  TimerMixin.setInterval(this.props.actions.transmitReadingStats, 5000);
}

componentWillUnmount() {
  TimerMixin.clearInterval(this.timer)
}

I also tied what @gre did with .call(... and got the same results

there's no reason that won't work, but if you're doing that, you might as well not even use the mixin--you're manually clearing them in componentWillUnmount. The whole point of this mixin is it automatically cleans up timers for you. So if what you pasted here isn't working, you're doing something else wrong, since you're clearing it yourself. Try using plain setInterval and clearInterval and you will see it has the exact same results.

Now, if you did above without clearing, as the mixin is intended (i.e. just the componentDidMount) part, then of course it wouldn't work. You called setInterval essentially on the prototype, rather than a component _instance_ that has it mixed in; and if you look here: https://github.com/reactjs/react-timer-mixin/blob/master/TimerMixin.js#L22 , you will see that the mixin doesn't know where to find this.timer; it clears an array of intervals stored at this. TimerMixin_intervals. So the net result is: the component instance's componentWillUnmount callback wouldn't be able to find the timer and wouldn't clear it. *TimerMixin must be attached to the instance`. I'm going to do some experiments with this stuff when I get a chance.

my pull request is in with the ES6 version:

PR #10

USAGE:

import {Timer} from 'react-timer-mixin';

class MyComponent extends Timer(React.Component) {
...
}

then call this.setTimeout/Interval as usual. No need to call super in your componentWillUnmountmethod.

Hey guys! I'm using the timer mixin for a "timer" like app. Unfortunately when I lock my screen or navigate away the timer essentially pauses since my "setInterval" is cleared and my redux reducers aren't hearing any dispatches.

Any good advice on how to go about fixing this with the mixin? I guess I would have to prevent clearing at unmount and only clear my "setInterval" when my timer completes.

Closing as essentially a duplicate of #4. You can track #12 to follow the progress on implementing a HOC which bring full ES6 class support.

If you don't want to wait for the implementation, this class handles multiple timeouts and intervals. Just call timer.clear() in componentWillUnmount to clear everything.

class timer {

	constructor() {

		this.timeouts = [];
		this.intervals = [];
	}

	setTimeout(callback, n) {

		const t = setTimeout(callback, n);

		this.timeouts[this.timeouts.length] = t;

		return t;
	}

	setInterval(callback, n) {

		const t = setInterval(callback, n);

		this.intervals[this.intervals.length] = t;

		return t;
	}

	clearTimeout(t) {

		if ( t ) {

			clearTimeout(t);
			this.timeouts = this.timeouts.filter(t1 => t1 !== t);
		} else {

			this.timeouts.map(clearTimeout);
			this.timeouts = [];
		}
	}

	clearInterval(t) {

		if ( t ) {

			clearInterval(t);
			this.intervals = this.intervals.filter(t1 => t1 !== t);
		} else {

			this.intervals.map(clearInterval);
			this.intervals = [];
		}
	}

	clear() {

		this.clearTimeout();
		this.clearInterval();
	}
}
export default new timer();

When I add 1 second it works but when I add more than 1 second it doesn't work ... take a look haha

componentDidMount() {
    console.log('gomotherfuckingtimer');
    // Try adding more than one second... rs! It's my life!
    const WAIT = 1000;
    this.timer = TimerMixin.setTimeout(() => {
      console.log('waaaaat');
      this.setState({redirectionTimeoutCompleted: true});
    }, WAIT);
  }

@cazetto I had same problem :/

commented

i put it in the component but timeout function not firing !?

// using TimerMixin
  componentDidMount() {
    this.setTimeout(()=>{
      this.state.situation = "Waiting"
    }, 3000)
  } 

@sophiebits Thank you, so, so, so much for your setTimeout / setInterval simple recommendation!