Callback Handlers
This section describes how ReactJS' <div onClick={this.handleClick} />
pattern translates into ReasonReact.
Callback Without State Update
Two scenarios.
self
Without Reading From Reminder: self
is ReasonReact's this
. It's a record that contains things like state
, send
and others.
If you're just forwarding a callback prop onto your child, you'd do exactly the same thing you'd have done in ReactJS:
let component = /* ... */;
let make = (~name, ~onClick, _children) => {
...component,
render: (self) => <button onClick=onClick />
};
No surprise here. Since Reason's JSX has punning syntax, that button
will format into <button onClick />
.
Similarly, to pre-process a value before sending it back to the component's owner:
let component = /* ... */;
let make = (~name, ~onClick, _children) => {
let click = (event) => onClick(name); /* pass the name string up to the owner */
{
...component,
render: (self) => <button onClick=click />
}
};
self
Reading From To access state
, send
and the other items in self
from a callback, you need to wrap the callback in an extra layer called self.handle
:
let component = /* ... */;
let make = (~name, ~onClick, _children) => {
let click = (event, self) => {
onClick(event);
Js.log(self.ReasonReact.state);
};
{
...component,
initialState: /* ... */,
render: (self) => <button onClick={self.handle(click)} />
}
};
Note how your click
callback now takes the extra argument self
. Formally, self.handle
expects a callback that
- accepts the single payload you'd normally directly pass to e.g.
handleClick
, - plus the argument
self
, - returns "nothing" (aka,
()
, aka,unit
).
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.
Explanation
In reality, self.handle
is just a regular function accepting two arguments, the first being the callback in question, and the second one being the payload that's intended to be passed to the callback.
Get it? Through Reason's natural language-level currying, we usually only ask you to pass the first argument. This returns a new function that takes in the second argument and executes the function body. The second argument being passed by the caller, aka the component you're rendering!
Callback Receiving Multiple Arguments
Sometimes, the component you're calling is from JavaScript (using the ReasonReact<->ReactJS interop), and its callback prop asks you to pass a callback that receives more than one argument. In ReactJS, it'd look like:
handleSubmit: function(username, password, event) {
this.setState(...)
}
...
<MyForm onUserClickedSubmit={this.handleSubmit} />
You cannot write such handleSubmit
in ReasonReact, as handle
expects to wrap around a function that only takes one argument. Here's the workaround:
let handleSubmitEscapeHatch = (username, password, event) =>
self.handle(
(tupleOfThreeItems, self) => doSomething(tupleOfThreeItems, self),
(username, password, event),
);
...
<MyForm onUserClickedSubmit=(handleSubmitEscapeHatch) />
Basically, you write a normal callback that:
- takes those many arguments from the JS component callback prop,
- packs them into a tuple and call
self.handle
, - pass to
handle
the usual function that expects a single argument, - finish calling
self.handle
by passing the tuple directly yourself.
Callback With State Update
You can't update state in self.handle
; you need to use self.send
instead. See the next section.