Lucifier129 / react-lite

An implementation of React v15.x that optimizes for small script size
MIT License
1.73k stars 98 forks source link

Updater seems to loose context when run through requestAnimationFrame #73

Open qpre opened 8 years ago

qpre commented 8 years ago

Hello, I'm currently having an issue I can't resolve by myself.

class MyComponent {

  ...

  handleRef(ref) {
    this.rootNode = ref;

    if (ref === null) {
      cancelAnimationFrame(this.animationFrameID);
      this.animationFrameID = null;
      return;
    }

    this.onAnimationFrame();
  }

  onAnimationFrame() {
    if (!this.rootNode) { return; }

    // computes stuff

    if (conditionOnStuff) { this.setState({ field: true }); } // <-- this explodes.

    this.animationFrameID = requestAnimationFrame(this.onAnimationFrame.bind(this));
  }

  render() {
    return <div ref={r => this.handleRef(r)} />;
  }
}

This component uses a ref on its root node, and depends on requestAnimationFrame for computing stuff around. when a condition is met, we call this.setState but it fails like this:

screen shot 2016-09-26 at 17 25 41 screen shot 2016-09-26 at 17 25 22

My understanding of the issue is that somehow track of my component's context was lost, but I don't have enough knowledge of react-lite's internals to put a finger on it.

Also, it works fine when I replace react-lite by react and react-dom.

Any idea of what could happen ?

Lucifier129 commented 8 years ago

Which version of react-lite did you use?

onAnimationFrame had better out of handleRef, just use handleRef to attach and detach ref

class MyComponent {
  componentDidMount() {
    if (condition) {
        this.onAnimationFrame()
    }
  }
  componentDidUpdate() {
    if (condition) {
        this.onAnimationFrame()
    }
  }
  componentWillUnmount() {
    this.cancelAnimationFrame()
  }
  handleRef(ref) {
    this.rootNode = ref;
  }
  cancelAnimationFrame() {
    cancelAnimationFrame(this.animationFrameID)
  }
  onAnimationFrame() {
    if (!this.rootNode) { return; }

    if (this.state.count < 10) {
        this.setState({ count: this.state.count + 1 });
    } // <-- this explodes.

    this.animationFrameID = requestAnimationFrame(this.onAnimationFrame.bind(this));
  }

  render() {
    return <div ref={r => this.handleRef(r)}>{this.state.count}</div>;
  }
}