purescript-react-basic-hooks icon indicating copy to clipboard operation
purescript-react-basic-hooks copied to clipboard

How to `forwardRef`?

Open i-am-the-slime opened this issue 5 years ago • 10 comments

I'd like to create ReactComponents which can take a ref prop or that are defined with React.forwardRef somehow. Do you have an idea how to do that?

i-am-the-slime avatar Aug 05 '20 14:08 i-am-the-slime

I tried to implement it and it got really messy. I'd probably just do it in FFI for now, unless you have an idea how to do it.

maddie927 avatar Aug 05 '20 16:08 maddie927

The problem was I either needed to add it to every component or duplicate the 3 or 4 component creation function almost exactly but with the ref in the types. Maybe having just one would be better than nothing.. I was hoping I'd come up with a better way to do it.

maddie927 avatar Aug 05 '20 16:08 maddie927

I tried in FFI and I'm not quite there yet. Do you have any leftovers?

i-am-the-slime avatar Aug 05 '20 16:08 i-am-the-slime

I don't think I saved the code. The simplest thing would be something like

foreign import mkForwardRefComponent ::
  forall props a.
  String ->
  ({| props } -> Ref a -> JSX) ->
  Effect (ReactComponent { ref :: Ref a | props })
exports.mkForwardRefComponent = (displayName) => (renderFn) => () => {
  const component = (props, ref) => renderFn(props)(ref);
  component.displayName = displayName;
  return React.forwardRef(component);
};

Something like that.. it's a little more complicated if you want hooks

maddie927 avatar Aug 05 '20 17:08 maddie927

(edited a few times, sorry, I think it works now)

maddie927 avatar Aug 05 '20 17:08 maddie927

This might work for hooks.. it's a little hacky, haven't tested:

foreign import mkForwardRefComponent ::
  forall props a hooks.
  String ->
  ({| props } -> Ref a -> Render Unit hooks JSX) ->
  Effect (ReactComponent { ref :: Ref a | props })
exports.mkForwardRefComponent = (displayName) => (renderFn) => () => {
  const component = (props, ref) => renderFn(props)(ref)();
  component.displayName = displayName;
  return React.forwardRef(component);
};

maddie927 avatar Aug 05 '20 17:08 maddie927

Thanks so much, I was fiddling in parallel and got something to somewhat work. I somehow thought the Ref needed to be typed more like (Nullable (Ref (Nullable Node))) :

foreign import forwardRef 
  :: forall props. 
    EffectFn2 {|props} (Nullable (Ref (Nullable Node))) JSX ->
    ReactComponent { ref :: Nullable (Ref (Nullable Node)) | props }

i-am-the-slime avatar Aug 05 '20 18:08 i-am-the-slime

If you use it as a prop to a DOM element it does, but they can be anything. Holding arbitrary values, used with useImperativeHandle (which would be a nightmare to type all on its own 😅), etc

maddie927 avatar Aug 05 '20 18:08 maddie927

Wow, I knew that in general they could be anything, but I assumed that for passing them with forwardRef they'd be monomorphised to Node stuff. Thanks for showing me useImperativeHandle. Alright, I think you're right and I will just write my own FFI in the project itself!

i-am-the-slime avatar Aug 05 '20 18:08 i-am-the-slime

Ok, I think we can just leave this open until it's supported somehow in case anyone else is wondering

maddie927 avatar Aug 05 '20 18:08 maddie927