fable-compiler / fable-react

Fable bindings and helpers for React and React Native
MIT License
275 stars 66 forks source link

Problems while writing a binding for Vizceral #51

Closed vasily-kirichenko closed 6 years ago

vasily-kirichenko commented 6 years ago

I'm trying to write a binding for https://github.com/Netflix/vizceral-react.

I just start with the Elmish template and add the following right into Counter/View.fs

[<RequireQualifiedAccess>]
type Vizceral =
    | AllowDraggingOfNodes of bool
    | ShowLabels of bool
    | TargetFramerate of int
    | Traffic of string
    interface IProp

let vizceralEl : obj = import "Vizceral" "vizceral-react"

let inline vizceral (props: IProp list) (childred: React.ReactElement list) : React.ReactElement =
    createElement (vizceralEl, keyValueList CaseRules.LowerFirst props, childred)

let data = """... a json copied from https://github.com/Netflix/vizceral-example/blob/master/src/sample_data_simple.json ..."""

let root model dispatch =
  div []
    [ vizceral [ Vizceral.Traffic data ] []  ]

(I tried to follow the approach I found in https://github.com/fable-compiler/fable-react/blob/master/src/Fable.Recharts/Fable.Recharts.f)

I've got this error at runtime:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of LazyView.

and a warning:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of LazyView. in LazyView

alfonsogarciacaro commented 6 years ago

The error message seems to indicate that undefined is being passed to React.createElement so apparently the import is not working. Please check the names are right (Vizceral and vizceral-react) and that you have installed the vizceral-react package with npm/yarn. If you're using Webpack, do you get any error/warning saying the Vizceral member is not exported from vizceral-react or similar?

vasily-kirichenko commented 6 years ago

No warnings:

 dotnet fable yarn-start
Fable (1.3.7) daemon started on port 61225
CWD: E:\github\Elmish1
cmd /C yarn run start
yarn run v1.0.2
$ webpack-dev-server
Bundling for development...
Project is running at http://localhost:8080/
webpack output is served from /
Content not from webpack is served from E:\github\Elmish1\public
Parsing ./Elmish1.fsproj...
fable: Compiled src\Elmish1.fsproj
fable: Compiled src\App.fs
fable: Compiled src\Home\View.fs
fable: Compiled src\Info\View.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.react\2.0.0-beta-002\fable\Fable.Helpers.React.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.browser\1.0.0\fable\navigation.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.debugger\1.0.1\fable\debugger.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish\1.0.1\fable\program.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.hmr\1.0.0\fable\hmr.fs
fable: Compiled src\Types.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish\1.0.1\fable\cmd.fs
fable: Compiled src\Counter\View.fs
fable: Compiled src\Global.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.react\1.0.1-beta-1\fable\react.fs
fable: Compiled src\Navbar\View.fs
fable: Compiled src\State.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.browser\1.0.0\fable\parser.fs
fable: Compiled src\Home\Types.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.react\1.0.1-beta-1\fable\common.fs
fable: Compiled src\Counter\State.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.debugger\1.0.1\fable\Fable.Import.RemoteDev.fs
fable: Compiled src\Home\State.fs
fable: Compiled src\Counter\Types.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.recharts\0.1.0-beta-002\fable\Fable.Recharts.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fulma\1.0.0-beta-005\fable\Elements\Button.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fulma\1.0.0-beta-005\fable\Common.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fable.elmish.browser\1.0.0\fable\prelude.fs
fable: Compiled C:\Users\vaski\.nuget\packages\fulma\1.0.0-beta-005\fable\BulmaClasses.fs
Hash: cdfbb53d3821ee40033e
Version: webpack 3.10.0
Time: 16095ms
        Asset     Size  Chunks                    Chunk Names
    bundle.js  7.26 MB       0  [emitted]  [big]  main
bundle.js.map  8.26 MB       0  [emitted]         main
[./node_modules/loglevel/lib/loglevel.js] ./node_modules/loglevel/lib/loglevel.js 7.86 kB {0} [built]
[./node_modules/strip-ansi/index.js] ./node_modules/strip-ansi/index.js 161 bytes {0} [built]
[./node_modules/url/url.js] ./node_modules/url/url.js 23.3 kB {0} [built]
[./node_modules/webpack-dev-server/client/index.js?http://localhost:8080] (webpack)-dev-server/client?http://localhost:8080 7.95 kB {0} [built]
[./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
[./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.05 kB {0} [built]
[./node_modules/webpack/hot ^\.\/log$] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[./node_modules/webpack/hot/dev-server.js] (webpack)/hot/dev-server.js 1.61 kB {0} [built]
[./node_modules/webpack/hot/emitter.js] (webpack)/hot/emitter.js 77 bytes {0} [built]
[./node_modules/webpack/hot/log-apply-result.js] (webpack)/hot/log-apply-result.js 1.31 kB {0} [built]
[./node_modules/webpack/hot/log.js] (webpack)/hot/log.js 1.04 kB {0} [built]
[./src/App.fs] ./src/App.fs 5.5 kB {0} [built]
[./src/Counter/View.fs] ./src/Counter/View.fs 3.66 kB {0} [built]
   [0] multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/Elmish1.fsproj 52 bytes {0} [built]
[./src/Elmish1.fsproj] ./src/Elmish1.fsproj 25 bytes {0} [built]
    + 875 hidden modules
webpack: Compiled successfully.

...and no mention of Vizceral.

If I change the import to

let inline vizceral (props: IProp list) (children: React.ReactElement list) : React.ReactElement =
    ofImport "Vizceral" "vizceral-react" (keyValueList CaseRules.LowerFirst props) children

then it's in the logs:

webpack: Compiling...
Parsing ./App.fs...
Parsing ./Counter/View.fs...
fable: Compiled src\Counter\View.fs
Hash: c49712fb7f580ddbe872
Version: webpack 3.10.0
Time: 4675ms
                                   Asset      Size  Chunks                    Chunk Names
                               bundle.js   7.26 MB       0  [emitted]  [big]  main
    0.4904a58fdd57b7a66562.hot-update.js   3.76 MB       0  [emitted]  [big]  main
    4904a58fdd57b7a66562.hot-update.json  43 bytes          [emitted]
                           bundle.js.map   8.26 MB       0  [emitted]         main
0.4904a58fdd57b7a66562.hot-update.js.map   4.64 MB       0  [emitted]         main
[./node_modules/vizceral-react/dist/vizceral.js] ./node_modules/vizceral-react/dist/vizceral.js 571 kB {0} [built]
[./node_modules/vizceral/dist/vizceral.js] ./node_modules/vizceral/dist/vizceral.js 3.17 MB {0} [built]
[./node_modules/webpack/hot ^\.\/log$] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[./src/Counter/View.fs] ./src/Counter/View.fs 3.63 kB {0} [built]
    + 886 hidden modules
webpack: Compiled successfully.

but with the same result.

vasily-kirichenko commented 6 years ago

This is the head of vizceral.js file:

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory(require("react"), require("vizceral"));
    else if(typeof define === 'function' && define.amd)
        define(["react", "vizceral"], factory);
    else if(typeof exports === 'object')
        exports["Vizceral"] = factory(require("react"), require("vizceral"));
    else
        root["Vizceral"] = factory(root["React"], root["vizceral"]);
})
alfonsogarciacaro commented 6 years ago

Ah, damn. I was trying to reproduce this and didn't understand until I checked vizceral-react README. The import should be made like this:

import Vizceral from 'vizceral-react';

This means it's importing the default member (which is different from importing the member Vizceral which would be import { Vizceral } from 'vizceral-react') so in Fable you need to do:

let vizceralEl : obj = importDefault "vizceral-react"

// Or...

let inline vizceral (props: IProp list) (children: React.ReactElement list) : React.ReactElement =
    ofImport "default" "vizceral-react" (keyValueList CaseRules.LowerFirst props) children

With this I'm not getting the error anymore and the React element seems to be mounted. I couldn't see anything on screen though.

BTW, the vizceral-react packages uses PropTypes so it's not compatible with React 16 (unless they change to the separate prop-types package), to prevent that you need to delete the lines with reference to PropTypes (L256-323 in dist/vizceral.js).

vasily-kirichenko commented 6 years ago

@alfonsogarciacaro Thanks you :) Sorry about such questions, I'm a total newbie in JS and React.

Your suggestions work - there are no errors anymore, however the visualization has not appeared yet :) I run the official example (it does work) and my app in parallel and try to find any differences...

alfonsogarciacaro commented 6 years ago

No worries, default member importing can be confusing in JS. The problem here is vizceral.js exporting is very dynamic to support different module systems, so Webpack doesn't realize the Vizceral member was missing.

If you need more help to identify the problem with the visualization please tell me. I used the React dev tools and the component seemed to be mounted.

vasily-kirichenko commented 6 years ago

The data in the Traffic field seems to be identical to the official example apart of what I thought should be of type int64 (official):

image

(mine):

image

updated field is documented:

// Unix timestamp. Only checked at this level of nodes. Last time the data was updated (Needed because the client could be passed stale data when loaded)
      updated: 1462471847,

What is the proper F# type for this "Unix timestamp" thing?

vasily-kirichenko commented 6 years ago

Removed update entirely, no changes.

vasily-kirichenko commented 6 years ago

My wip is here https://github.com/vasily-kirichenko/Elmish1/blob/master/src/Counter/View.fs if you like to look at it.

MangelMaxime commented 6 years ago

@vasily-kirichenko I think in my Fable application, I am storing timestamp using a float type.

alfonsogarciacaro commented 6 years ago

Fable uses a custom type for Long in order to have the full 64 bits range, if you need to compile to JS number I suggest using int or float.

vasily-kirichenko commented 6 years ago

Thanks, guys. float works. But still no visualization :)

alfonsogarciacaro commented 6 years ago

@vasily-kirichenko Where's the official example you're comparing against? There's some code in vizceral-react README but it's missing all the data. I also found this, but it's too complex: https://github.com/Netflix/vizceral-example

I managed to visualize something using your code, by manually setting height: 500px to the element with the vizceral class. You may need to add some styling to the app for that.

screen shot 2018-01-04 at 11 30 36 am
vasily-kirichenko commented 6 years ago

Yes! It finally works :) Thank you a lot for the help.

alfonsogarciacaro commented 6 years ago

Glad to be of help :) Please do share the bindings and a sample with the community!

vasily-kirichenko commented 6 years ago

Yes, I will.