What is self.handle for in ReasonReact?

reasonreact

#1

In the ReasonReact docs, under Callback Handlers, it says this:

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

But then, under State, Actions & Reducer:

In render, instead of self.handle (which doesn’t allow state updates), you’d use self.send. send takes an action.

In my code, I often forget about self.handle, and just pass a callback to a child component like this:

onLayout={event => self.send(
  UpdateHeight(event->BsReactNative.RNEvent.NativeLayoutEvent.layout.height)
)}

or like this:

onSubmit={() => submit(self.state)}

It works fine, at least as far as I can tell. Am I storing up problems for future Ben? Should I be using self.handle? And if so, exactly when should I be using it?

Thanks for any insight you can give!


#2

Hmm. I’d like to know that too. The difference is really not that clear from the docs.

If anything, self.send effectively calls self.handle after putting the action through the reducer and calling setState. That explains why you cannot set state in self.handle, but what can you do in it? Side effects?

I mean, it does look it’s for side effects, but maybe a couple of realistic examples in the docs would add some clarity.


#3

I don’t understand though why it’s needed. If you’re inside the render function, you already have access to self.state. Why would you use self.handle, even for side effects?


#4

I did some more reading :slight_smile:

Going by the source code, self.handle aggregates JS state, Reason state, JS & Reason props, debugName and some more stuff and calls your callback with all of that. It also seems that ReasonReact’s render also aggregates all that before creating a component and calling React.render, so maybe, just maybe, () => submit(self.state) is enough in your case.

But then React is increasingly asynchronous, so, in theory, the self.state from your last render cycle could get stale. Not likely if you use controlled inputs, but who knows. Probably it’s simpler to play safe and use callbacks that give you a fresh state, guaranteed.

Mind you, I’m not an expert, so let’s wait for a second opinion :slight_smile: And thanks for reminding me I should read the sources more!


#5

Thanks for that, that clarifies the ReasonReact source quite a lot for me, I was having trouble following what it was doing. I’m not used to reading these pub methods in reason!

However, it’s still not clear to me what benefit handle is actually providing. As I understand it, an update to state will always trigger render (as long as it was performed via setState or, in this case, reducer). Therefore, if you’re passing down a callback to a child component which relies on the value of state, and you’re doing it in render, when state gets updated, render will get called again and you’ll pass a new callback down to your child component. Therefore, there’s no danger of state being stale.

Even with upcoming Concurrent/Async React, the contract between render and state will remain – inside render, state must be reliably fresh. This is surely the fundamental point of react, that what you render is a function of your state at any given moment. The react docs themselves say:

setState() will always lead to a re-render unless shouldComponentUpdate() returns false

I feel like I must be missing something glaringly obvious, because handle must have been implemented for a reason. But I just can’t grasp what it might be!


#6

Well, there you have it! :slight_smile: You can change state and bail out of rendering, so even if your component never does at the moment, using self.state directly makes it more brittle.

But again, I’m no expert, so maybe that’s not the only reason.


#7

True :slightly_smiling_face: but that’s also true of reading from state in render in reactJS, and I don’t think they offer an equivalent of self.handle. So I’m left with the same question, why do we have it in Reason react!


#8

Yeah, but with class-based ReactJS at least, methods like this.handleSubmit have access to this.state at the moment of event, not at the moment of rendering. And ReasonReact doesn’t give you this.


#9

True, but you can pass an arrow function as an event handler without any issues. Hope it’s not too cheeky to say that it would be amazing to get @chenglou’s input on this!