Docs / ReasonReact / CallbackHandlers

Callback Handlers

The Record API is in feature-freeze. For the newest features and better support going forward, please consider migrating to the new function components.

This section describes how ReactJS' <div onClick={this.handleClick} /> pattern translates into ReasonReact.

Callback Without State Update

Two scenarios.

Without Reading From self

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:

RE
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:

RE
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 /> } };

Reading From self

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:

RE
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:

JAVASCRIPT
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:

RE
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.