React Hooks support in ReasonReact

bsb
reasonreact

#1

Hey folks,

0.7.0 of ReasonReact is out with support for Hooks and function components. If you’re interested in trying this out please read the blog post and get started. Make sure you are using ^5.0.1 version of bs-platform and that you set your JSX to version 3 when using it with a new component.

If you have any questions or issues with it, feel free to ask here or message me on twitter/discord!


Hooks coming to Reason React?
#2

This new API looks quite interesting. It looks simpler than the Records-based one, but I’m worried about the behind-the-sences magic which seems to require following some usage rules not enforceable by the type system. I guess I will have to try it out.

The ReasonReact documentation for Hooks seems half-finished at best though. Where is the documentation for the various kinds of hooks and their types?

Also, the deprecation of the Records API seems strange to me. In the React docs it is made clear that class-based components are not going away anytime soon, so why are they deprecated in Reason?


#3

The most valuable reason would be to have a zero runtime cost interop between React component and Reason’s component. The previous version wrap the component in some glue code that adds runtime costs if you crossing the border many times


#4

Can you supply some specific examples of what you cannot do with ReasonReact and hooks?


#5

I’m worried about the behind-the-sences magic which seems to require following some usage rules not enforceable by the type system

Yup this is a legitimate concern. I feel similarly and I think we are definitely open to enforcing with types. But I’d like to see some of this work happening in the community because I think there are a few ways to encode this information and it doesn’t make sense to force a decision without folks trying them.

Where is the documentation for the various kinds of hooks and their types?

We link to the ReactJS hooks docs pretty aggressively because they already have pretty thorough guidance. I want to be sure to document everything that differs, but I’m not sure it makes sense to repeat on the RR page. A huge benefit of this change is that now writing a component is basically the same as in JS. Much less that’s different to document.

deprecation of the Records API seems strange to me

Deprecation is a was a poor word choice. My apologies. Think of it as feature frozen. Will change anywhere I see it in the docs. ReasonReact benefits much more from this switch than React does.


#6

The biggest one I know of is error boundaries. There are a couple more minor things (like second callback in set state).


#7

Yup this is a legitimate concern. I feel similarly and I think we are definitely open to enforcing with types. But I’d like to see some of this work happening in the community because I think there are a few ways to encode this information and it doesn’t make sense to force a decision without folks trying them.

That’s good to hear. I look forward to what happens here.

We link to the ReactJS hooks docs pretty aggressively because they already have pretty thorough guidance. I want to be sure to document everything that differs, but I’m not sure it makes sense to repeat on the RR page. A huge benefit of this change is that now writing a component is basically the same as in JS. Much less that’s different to document.

Well, as far as I can tell there are decisions made in the typing of the functions which by definition cannot be found in the JS docs. It makes sense to not repeat it, but I think documenting the type signatures would be really useful. I tried looking them up on Github, and besides being inconvenient, they were not at all obvious to me - a lot of function and unit arguments which are not self-documenting.

Deprecation is a was a poor word choice. My apologies. Think of it as feature frozen. Will change anywhere I see it in the docs. ReasonReact benefits much more from this switch than React does.

That sounds great! I look forward to experimenting with hooks :slight_smile:


#8

Looks great, I’m really excited to try it. I have some questions about Context and Memo.

I saw in React.re file Context and memo are included but the docs still say Context API isn’t available in ReasonReact. ls this just a case of the docs not being caught up to the code?


#9

Well, I can’t find a way to use useRef for example, this :

[@react.component]
let make = (~foo: t) => {
    let myRef = React.useRef(None);
    <div ref={myRef}></div>
};

throws :

Error: This expression has type React.Ref.t(option('a)) 
       but an expression was expected of type ReactDOMRe.domRef

#10

For those wondering, here is the solution as suggested by alex.fedoseev on discord:

[@react.component]
let make = (~foo: t) => {

    let myRef = React.useRef(Js.Nullable.null);

    React.useEffect0(() => {
      myRef -> React.Ref.current
            -> Js.Nullable.toOption
            -> Option.map(myRef => /* use myRef of type Dom.element */)
            ->ignore;
      None;
    });

    <div ref={myRef -> ReactDOMRe.Ref.domRef}></div>
};

How to attach ref to a dom node with useRef hook
#11

How to use React.memo ?


#12

You can just use it as you would in JS. Have you got errors?


#13

I had tried a few days ago and I was getting errors with the ppx.

But now it works


#14

For those hitting issues with syntax highlighting (Reason Language Server) -

If your project is configured to use JSX2 in your bsconfig.json, and you have a file-level [@bs.config {jsx:3}] in your component module, you will find that your in-editor help will throw an error when copying the solution above, even though it compiles fine with bsb on the command line.

The error goes away when you switch your bsconfig.json to JSX3. However, that may break you elsewhere. Ultimately, it seems like RLS doesn’t respect the inline [@bs.config] values.

The result is that it tries to type match against the JSX2 version of the element transform—which calls ReasonReact.element, expecting ~ref to be Js.nullable(reactRef) => unit—instead of the JSX 3 version, expecting ~ref to be a ReactDOMRe.domRef.