3

All:

I am pretty new to React. When I follow its TodoMVC example, there is one question confuses me so much:

Inside TodoApp component, it registers its handler using TodoStore.addChangeListener:

componentDidMount: function() {
    TodoStore.addChangeListener(this._onChange);
  },
_onChange: function() {
    this.setState(getTodoState());
  }

And let CHANGE_EVENT in TodoStore.addChangeListener to trigger that _onChange callback:

addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
  },

What confuses me here is:

  1. How "this.on" knows the context of that callback( I mean how does it remember the "this" from _onChange )

  2. What if there are multiple TodoApp register their _onChange, so the todoStore will maintain a list of all callbacks?

Thanks

Kuan
  • 11,149
  • 23
  • 93
  • 201

1 Answers1

4
  1. It doesn't. the this in this.on refers to the Store. The provided callback references the _onChange function, which uses its own this. For it to work it will need to have been bound to the TodoApp. I think that example comes from the autobinding era of React. Nowadays you would use a .bind either in the componentDidMount or the class constructor.

Local Bind

componentDidMount: function() {
    TodoStore.addChangeListener(this._onChange.bind(this));
  },

OR class bind (the = () => creates an arrow function for the class, closing this at the constructor scope)

class TodoApp extends React.Component {
  componentDidMount = () => {
   TodoStore.addChangeListener(this._onChange);
  }

  _onChange = () => {
    this.setState(getTodoState());
  }
}
  1. Yes. In Flux Stores are EventEmitters, and are designed to have multiple listeners for each event.
Kyeotic
  • 19,697
  • 10
  • 71
  • 128
  • Thanks, Yeah, that "this._onChange.bind(this)" is what I think it should be too. But why the current code without it works too, or Do you mean that code actually not work? I thought if there is no explicit context bind, then when call this.on, the callback will be called with window as its context, right? – Kuan Nov 30 '15 at 19:35
  • 1
    React used to have a feature, on by default, called [Autobind](https://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html), that worked with `React.createClass`, and called `bind` on every function. This code would have worked with that feature. – Kyeotic Nov 30 '15 at 21:05
  • thanks. This is amazing, so this means even we define the _onChange: function() { this.setState(getTodoState()); } like this, but actually this._onChange is something like function() { this.setState(getTodoState()); }.bind(this) ? – Kuan Nov 30 '15 at 21:25
  • Yes, but this this feature isn't on by default anymore, and isn't even available for the ES6 Class syntax. For ES6 you can either use the arrow syntax I showed above, or call `bind` in the constructor. – Kyeotic Nov 30 '15 at 21:27
  • so this code probably written long time ago and not work now if that default feature is not on. Anyway, I get the babsic idea. Thanks, this very helpful – Kuan Nov 30 '15 at 21:30