Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Querying a React component #107

Open
karls opened this issue Feb 26, 2017 · 18 comments
Open

Querying a React component #107

karls opened this issue Feb 26, 2017 · 18 comments

Comments

@karls
Copy link
Contributor

karls commented Feb 26, 2017

Hi,

I've been using Elm a lot, but due to the inability to tap into the vast sea of JS/React libraries I have started to look into PureScript and Pux.

I managed to mount https://github.com/zenoamaro/react-quill inside my toy Pux app, but I can't seem to work out how to query the state of the component. I would like to be able to keep track of the contents of the editor in my Pux model (in order to persist them in the DB etc). Is it currently possible with Pux? I haven't used React either, so I might be missing something obvious.

Many thanks!

@chexxor
Copy link
Contributor

chexxor commented Feb 26, 2017 via email

@karls
Copy link
Contributor Author

karls commented Feb 26, 2017 via email

@chexxor
Copy link
Contributor

chexxor commented Feb 26, 2017 via email

@karls
Copy link
Contributor Author

karls commented Feb 27, 2017

The problem I'm having is I can't see how to notify Pux of changes happening inside the React component. I've put together a gist with some of my code here https://gist.github.com/karls/dc25b4ed6a8837b7382e97afdb462423. Essentially, I would like to update my Pux model's notes field whenever the contents of the editor change.

As a disclaimer, I'm fairly certain that it's down to my inexperience with PS/Pux and React. I'm sure there is an obvious way to do this, but I can't seem to work out how. In Elm-land, the recommended approach is to use Ports to communicate with Javascript -- e.g subscribe to changes in a value that comes from a Javascript source.

Although the documentation doesn't say that, I saw in the source that there is an actionChannel in the App type: https://github.com/alexmingoia/purescript-pux/blob/master/src/Pux.purs#L109. Is that what you meant by sending events to the action channel?

@karls
Copy link
Contributor Author

karls commented Feb 28, 2017

Just an update from my side.

I managed to get this working, more-or-less. I've updated the gist as well with code that works.

I ended up doing it with a custom onChange handler. But as you pointed out, the React component's onChange is not a standard onChange event -- its signature is onChange(content, delta, source, editor). Luckily, content is what I was interested in, so I could just pass a 1-argument event handler in my own code.

It would be nice to document this somehow. E.g, what if I also wanted to do something with the other 3 arguments in the component's onChange handler?

@chexxor
Copy link
Contributor

chexxor commented Feb 28, 2017 via email

@karls
Copy link
Contributor Author

karls commented Mar 1, 2017

Ah, yes, I saw your pull request now.

I had a quick play around with the type of event handler as in your pull request. I couldn't make it work.. I tried having the handler itself as a function using Data.Function.Uncurried, but without success.

I suspect I'm missing something obvious, because I'm not sure how I would pass all the arguments as an array. The onChange arguments are all different types, but PureScript arrays are homogeneous.

@karls
Copy link
Contributor Author

karls commented Mar 1, 2017

Another realisation. If I understand this correctly, the handler function in Pux.Html.Events assumes that the event handler will receive a single argument ev, which corresponds to a JS Event interface. However both in my example with react-quill and in your example with react-bootstrap-date-picker in your pull request, the handler will receive more than one argument.

I think that even with the changes in your pull request it won't help with defining event handlers that receive more than one argument, because you have re-used handler from Pux.Html.Events which assumes that a single argument, a JS Event is passed.

Ideally, components wouldn't deviate from the way standard JS events are handled, but is there something we can do to make it possible to handle such strange, non-standard "events" in Pux?

@chexxor
Copy link
Contributor

chexxor commented Mar 1, 2017

@karls - I mean adding some functions like below:

Events.purs

evt' :: forall event action. String -> (event -> action) -> Attribute action
evt' eventName = runFn2 handlerCustom eventName

foreign import handlerCustom :: forall args a. Fn2 String (Array args -> a) (Attribute a)

Events.js

exports.handlerCustom = function (key, action) {
  return [key, puxHandler(function (input, parentAction) {
    return function () {
      if (arguments) {
        var ev = arguments[0];
        if ((key === 'onSubmit')
        || (key === 'onClick' && ev.currentTarget && ev.currentTarget.nodeName.toLowerCase() === 'a')) {
          ev.preventDefault();
        }
        input(parentAction(action(arguments)))();
      }
    };
  })];
};

And used like this, but with better types to describe the value handled by the Action.

MyApp.purs

data Action
  = HandleManyArgsEvent (Array eventArgs)

update (HandleManyArgsEvent eventArgs)
  = { state: state {
          eventValue1 = arg1
        , eventValue2 = arg2
        , eventValue3 = arg3
        }
     , effects: []
     }
     where
       arg1 :: { data :: String, meta :: String }
       arg1 = unsafeCoerce (eventArgs !! 1)
       arg2 :: Boolean
       arg2 = unsafeCoerce (eventArgs !! 2)
       arg3 :: SomeOtherType
       arg3 = unsafeCoerce (eventArgs !! 3)

view =
  div []
    [ CustomComponent
      [ className "someAttributeValues"
      , evt' "SomeCustomEvent" HandleManyArgsEvent 
      ]
      [ p [] [ text "some children" ]
      ]
    ]

@karls
Copy link
Contributor Author

karls commented Mar 2, 2017

I tried this out and this actually works rather well. The only thing I needed to change was the type signature for evt', which was giving a type error on the event type variable.

That unsafeCoerce makes me sad. When/if the underlying component changes its API with a version upgrade it'll crash at runtime, right? Then again, upgrading dependencies blindly is never good.

@chexxor
Copy link
Contributor

chexxor commented Mar 2, 2017 via email

@karls
Copy link
Contributor Author

karls commented Mar 3, 2017

Yes, sorry. I was in a hurry when I left that comment.

I've created an example project demonstrating the mechanism you've outlined https://github.com/karls/pux-react-component-test.

@i-am-the-slime
Copy link

I would like to do the same but with the current version.

@limick
Copy link

limick commented Aug 14, 2017

+1 for current version. I've spent a few fruitless hours on this and would very much appreciate any hints.

@limick
Copy link

limick commented Sep 14, 2017

Concerning doing the same with Pux version 10 I managed to integrate react-quill@1.1.0.

From my limited understanding, the problem seems to have been that Pux expects your Action values to take arguments of type DOM.Event.Types.Event, but the react-quill component returns a String.

What worked for me was using
import Unsafe.Coerce as U
and then in the event handler:
foldp (DescriptionChange ev') state = let s = U.unsafeCoerce ev' :: String in ...
since I only needed the target value of the Event anyway.

The editor itself was integrated with something like:
MyEditor.component {value: p.description} #! onChange DescriptionChange $ mempty
Note that passing {onChange: DescriptionChange, value: p.description} instead did not work.

@borsboom
Copy link
Contributor

borsboom commented Oct 3, 2017

@limick: would you be able to post a working code example (like an updated https://github.com/karls/pux-react-component-test)?

@limick
Copy link

limick commented Oct 3, 2017

@borsboom: Sure, see https://github.com/limick/pux-react-component-test/tree/pux-v11. I'm not very familiar with the node ecosystem, so I couldn't get npm run watch to work again. However, everything that's needed is there and the code compiles. The use of unsafeCoerce is not very elegant, but it works for me.

@borsboom
Copy link
Contributor

borsboom commented Oct 3, 2017

@limick Thank you!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants