"Unbound record field send" in event handlers


#1

Following the instructions here, I’ve written an event handler of the form:

  let onChangeGame = (ev, self) =>
    self.send(/*blah*/);

And later in render, I have:

  <select onChange=(self.handle(onChangeGame))>/*blah*/</select>

And yet I’m getting the following compilation error:

Unbound record field send

If I change self.send to self.ReactReason.send as suggested here, the code compiles. However, the aforementioned docs seem to suggest that this shouldn’t be necessary, since in the code snippet it’s using self.state directly.

So I guess my question is if using self.ReactReason.send is necessary in event handlers if you want to send an action?


#2

The compiler can only inference based on the hints you give it. So writing self.ReasonReact is a way to give the compiler more hints. It’s not necessary or unnecessary here just add it when the compiler can’t figures out


#3

@thangngoc89 thanks for the response. I guess I was just wondering if it’s possible to give the compiler sufficient hints through some other means so that I don’t have to type out self.ReasonReact.send every time I want to send an action.

Also on a related note: what does self.ReasonReact mean exactly? Is there a field called ReasonReact in self, or is it just a way to tell the compiler that the record is defined in the ReasonReact module or something? I guess this is more of a general Reason question.


#4

self.ReasonReact.send tells the compiler that the send field is found in the ReasonReact module. If you instead destructure a record, like if you did this instead:

  let onChangeGame = (ev, { ReasonReact.send, state }) =>
    send(/*blah*/);

you’d only have to qualify one of the fields. The others would be inferred.

You can also accomplish this by opening the module:

  let onChangeGame = (ev, self) =>
    ReasonReact.(self.send(/*blah*/));

or

  let onChangeGame = (ev, self) => {
    open ReasonReact;
    self.send(/*blah*/);
  };

or by annotating the type of the argument. But in this case I doubt you want that, since the type of self is pretty complicated.


Question on why type inference isn't working
#5

That’s a good point, the doc example might be wrong in this case.

Normally I’d say that inside a record spread ({...component, render: (self) => <button onClick=self.hand...), you can access the record member directly because the compiler knows the type while it’s doing the spread update. But outside of the spread update, you have to help it by telling it which module the record field comes from, because that might be ambiguous.

So to recap, the doc might be wrong here, we’ll need to check and fix if so.


#6

I just stumbled upon that same issue yesterday - I am glad I’ve found this thread. Also, looks like the docs are now updated as they explicitly point out:

Note 2: sometimes you might be forwarding handle to some helper functions. Pass the whole self instead and annotate it. This avoids a complex self record type behavior. See Record Field send/handle Not Found.

Source: https://reasonml.github.io/reason-react/docs/en/callback-handlers.html

Right now, I’ve been using lambda inside render to sort of do the same - I am wondering if there are any performance implications different than what we normally would face with React?


#7

For performance implications, see https://stackoverflow.com/q/48885532/20371


#8

I’ve clarified the message in the new bucklescript release: https://github.com/BuckleScript/bucklescript/pull/2541