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

react: how to use 3rd party React component #60

Open
pjebs opened this issue Sep 1, 2018 · 13 comments
Open

react: how to use 3rd party React component #60

pjebs opened this issue Sep 1, 2018 · 13 comments

Comments

@pjebs
Copy link

pjebs commented Sep 1, 2018

If I want to use this component (React component):
https://github.com/frontend-collective/react-sortable-tree

Would I need to create "wrapper" structs etc for it in order to use it. Is there some sort of "escape hatch" I can use so I can use the component directly?

@myitcv myitcv changed the title how to use 3rd party React component react: how to use 3rd party React component Sep 6, 2018
@myitcv
Copy link
Owner

myitcv commented Sep 6, 2018

You don't need, per se, to create a wrapper like https://github.com/myitcv/x/blob/df0e6d2b54fffdff5ee716f1fa171381cfdeddf6/highlightjs/highlightjs.go

Instead you could write a Go-based react component that "wrapped" and used the third party component directly via use of https://godoc.org/github.com/gopherjs/gopherjs/js

Does that help to answer your question?

@pjebs
Copy link
Author

pjebs commented Sep 19, 2018

I still can't see how to use "react" components. In JS we use 'react.createcomponent' (when not using JSX). After importing a react component using npm, I don't know what to do next.

@mrksmrtn
Copy link

I have been trying to accomplish the same thing. I have put together a complete Gist with a simple JS component that I think does what @myitcv is getting at. The main action is in exttest.go in the Render() function. You just need your JS object to return a React Element.

Where I am stuck is in using an actual component written for React. Following the gopherjs FAQ entry for using an external library. It explodes just trying to use the component in my JS console (with the same createElement() call and no JSX). But I'm pretty ignorant about the whole Node.js, etc. ecosystem (not that that has bothered me before).

I'd love to take advantage of the plethora of existing JS components out there for a new project while writing sweet, sweet Go code.

@ajayep
Copy link

ajayep commented Sep 23, 2018

@myitcv Could you please provide an example for using existing React components ?

@myitcv
Copy link
Owner

myitcv commented Sep 24, 2018

Thanks for the interest. I'll try and get around to writing up an example this week.

@mrksmrtn
Copy link

@myitcv: That would be great. Or, I could be nearing something that is PR worthy.

I've updated my Gist with a working, external component example. I made a component with the LikeButton code from the React tutorial that webpack processes. Then a second generator in the Go code generates app.js with the Go code and the output of webpack like_button.js (per the gopherjs FAQ).

I wanted to use something from npm, but the trouble that I'm encountering now is with the export method.

This export method (shown in the gopherjs FAQ entry) creates global objects and works just fine. I have yet to find a npm component using it though (to avoid namespace pollution, no doubt):

exports.Foo = Foo;

Whereas other methods, do not:

modules.exports = Foo

Or:

exports.default = Foo;

Or:

export * from './Foo';

I did get a npm module working by changing its export method to the global method, but there has to be a better way than modifying npm sources. However, I haven't found a workaround yet. Hopefully the solution is obvious to someone less ignorant about JavaScript modules.

@mrksmrtn
Copy link

I've found a workaround for exporting a global from webpack. Simply create a wrapper .js file and set the object in the window object:

import TimeAgo from 'react-timeago';
window.TimeAgo = TimeAgo;

A pretty obvious solution as I was expecting. Maybe not the best one though?

I've created a new Gist from my previous example code that wraps react-timego.

@myitcv
Copy link
Owner

myitcv commented Sep 26, 2018

@mrksmrtn that's pretty much exactly the kind of thing I had in mind. In your example, your TimeAgo component is responsible for the "marshaling" of props/state to/from Go types to the underlying JavaScript types.

@myitcv
Copy link
Owner

myitcv commented Sep 26, 2018

Indeed @mrksmrtn it would be great if you could submit something like your gist as an example. Would you be happy to do that?

@mrksmrtn
Copy link

Sure thing.

I was playing a bit more with marshalling the Go props to JS. I overlooked the fact that GopherJS time.Time converts to JavaScript Date. So I tried passing the props struct directly. Unfortunately, TimeAgo expects date as the name of the prop. If I change the case of the struct field, then it's not exported from Go and I get an empty object in JS world.

The field can be tagged with js:"date" but alone that has no effect and requires writing some kludgy code like this:

type TimeAgoProps struct {
    *js.Object
    Date string `js:"date"`
}

...

ts := TimeAgoProps{Object: js.Global.Get("Object").New()}
ts.Date = time.Now()

A *js.Object embedded struct is needed and then the additional fields must be set separately.

Unfortunately, even if one wants to go through all that, the code still doesn't work because of gopherjs/gopherjs#433. You might be aware of some of this because I've seen your work digging into GopherJS' internalizing/externalizing.

Long story short, it would be nice if there were a way to modify struct field names in the resulting JS object (downcase them at least). Nothing jumped out at me on a cursory look at the compiler code though.

@myitcv
Copy link
Owner

myitcv commented Sep 27, 2018

@mrksmrtn myitcv.io/react components won't work with *js.Object special struct types. Because at runtime values of such types are not Go structs, but JavaScript objects. Hence the struct value semantics we rely on for detecting props/state having changed break.

But I don't think this is a loss at all, rather a gain. Because the "wrapper" component will only re-render if its props/state have changed according to those struct value semantics. Hence to do the marshalling within Render doesn't present that much of an additional cost. Indeed it's a cost you would need to bear in any case because you're crossing the boundary between Go/JavaScript.

For event callbacks you'll need to unmarshal from JavaScript back to Go obviously and then call onwards into the Go world.

If we start to see patterns emerge here in terms of the marshalling/unmarshalling I suspect we can add tooling to help, but let's see how we get on doing things by hand to start with (which is the approach I followed with myitcv.io/react in the first place).

Thanks again for looking into this.

@pjebs
Copy link
Author

pjebs commented Oct 17, 2018

Closing because I created my own bindings: https://github.com/rocketlaunchr/react/

@pjebs pjebs closed this as completed Oct 17, 2018
@myitcv
Copy link
Owner

myitcv commented Oct 19, 2018

@pjebs - I'm going to re-open this because of the comments from @mrksmrtn above and because it's relevant to #74

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

No branches or pull requests

4 participants