myitcv / x

Mono-repo for all myitcv.io/... Go code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

react: how to use 3rd party React component

pjebs opened this issue · comments

commented

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?

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?

commented

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.

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.

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

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

@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.

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.

@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.

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

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.

@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.

commented

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

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