This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-08-23
Channels
Hi there.
I'm faced with a problem using reagent + shadow-cljs + foreign React package.
Some package provides additional JSX tags, and I can't find to use in hiccup form.
Example:
react-three-fiber
package provides some JSX tags. e.g. mesh
, boxBufferGeometory
and so on.
I want to use them as below:
clojure
(ns my.package
(:require
[reagent.core :as rc]
["react" :refer [useRef]]
["react-three-fiber" :refer [Canvas]]))
(defn my-component
[]
(let [mesh-ref (useRef)]
(rc/as-element
[:> Canvas
[mesh
[boxBufferGeometory {:attach "geometory"
:args [1 1 1]}]]])))
But reagent can't recognize mesh
tag.
Anyone have solutions?@sakurai.yuta use (:require ["react-three-fiber" :as three])
and then [:> three/Canvas [:> three/mesh ...]]
and so on. they are all in the package so you either use the namespace alias or :refer
them
Got these errors:
Uncaught Error: Assert failed: Invalid Hiccup form: [nil {:attach "geometry", :args [1 1 1]}]
boxBufferGeometory
defined in react-three-fiber/src/three-types.ts
.
Referenced code is below:
declare global {
namespace JSX {
interface IntrinsicElements {
...omit...
mesh: ReactThreeFiber.Object3DNode<THREE.Mesh, typeof THREE.Mesh>
...omit...
boxBufferGeometry: ReactThreeFiber.BufferGeometryNode<THREE.BoxBufferGeometry, typeof THREE.BoxBufferGeometry>
...omit...
}}}
In react-three-fiber
JS examples, seems no need to import/require mesh
, boxBufferGeometory
and so on.
Maybe these are not Javascript Class, sugar-syntax for JSX template.apparently react-three-fiber does some jsx magic that converts into three.js expressions:
> <mesh />
simply is another expression for `new THREE.Mesh()`
it makes sense that [:mesh ..]
should work given that the JSX rules are to treat all lower-case tags as regular html tags. eg <div ...>
becomes React.createElement("div", ...)
the quote was from here: https://github.com/react-spring/react-three-fiber#does-it-have-limitations
ah. I think just comparing to what is going to happen under the hood at runtime. not what the actual JSX produces.
JSX can't express :div
vs Component
(keyword vs symbol) so I think their logic is lower-case = html tag, upper case = component
Umm... I tried first the code:
[:mesh ... [:boxBufferGeometry ...]]]
and got errors:
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, or you might have mixed up default and named imports.
@U3T4X9F8X
Thank you for reply.
Thats what I thought I should to use raw THREE.Mesh()
.
Maybe the method works well.
I think reason why is reagent find the mesh
and can't recognize the tag is additional JSX tag.
Basically React component is impletented as Javascript Class, so we can import and use in hiccup forms, but react-three-fiber
don't use this approach.I will try using raw THREE.Mesh()
instead of mesh
JSX tag.
Thank you all!
are you sure you didn't have any leftover [mesh ...]
or so? :mesh
definitely doesn't leave behind a undefined
while mesh
would
Tried code:
(let [mesh-ref (useRef)]
(rc/as-element
[Canvas
[mesh {:ref mesh-ref
:scale [1 1 1]}
[boxBufferGeometry {:attach "geometry"
:args [1 1 1]}]
[meshStandardMaterial {:attach "material"
:color "hotpink"}]]])))
And got compiler warnings:
------ WARNING #1 - :undeclared-var --------------------------------------------
File: /home/***/src/main/myns/code.cljs:16:9
--------------------------------------------------------------------------------
13 | (let [mesh-ref (useRef)]
14 | (rc/as-element
15 | [Canvas
16 | [mesh {:ref mesh-ref
---------------^----------------------------------------------------------------
Use of undeclared Var myns/mesh
--------------------------------------------------------------------------------
17 | :scale [1 1 1]}
18 | [boxBufferGeometry {:attach "geometry"
19 | :args [1 1 1]}]
20 | [meshStandardMaterial {:attach "material"
--------------------------------------------------------------------------------
and same 2 warnings(`boxBufferGeometory`, meshStandardMaterial
).
Got the error when executed:
Uncaught Error: Assert failed: Invalid Hiccup form: [#js {"$$typeof" #object[Symbol(react.memo)], :type #object[Function], :compare nil} [nil {:ref #js {:current nil}, :scale [1 1 1]} [nil {:attach "geometry", :args [1 1 1]}] [nil {:attach "material", :color "hotpink"}]]]
this Assert failed: Invalid Hiccup form
is completely expected after Use of undeclared Var myns/mesh
(let [mesh-ref (useRef)]
(rc/as-element
[:> Canvas
[:mesh {:ref mesh-ref
:scale [1 1 1]}
[:boxBufferGeometry {:attach "geometry"
:args [1 1 1]}]
[:meshStandardMaterial {:attach "material"
:color "hotpink"}]]])) )
although I'm not too sure about :scale
and :args
. maybe that needs to be :scale #js [1 1 1]
Finally, the code:
(defn fn$animation-lantern
[]
(let [mesh-ref (useRef)]
(rc/as-element
[:> Canvas
[:mesh {:ref mesh-ref
:scale [1 1 1]}
[:boxBufferGeometry {:attach "geometry"
:args [1 1 1]}]
[:meshStandardMaterial {:attach "material"
:color "hotpink"}]]])))
Got error:
Uncaught TypeError: lastCallbackNode is not a function
at flushFirstCallback (scheduler.development.js:108)
at flushImmediateWork (scheduler.development.js:170)
at Object.exports.unstable_runWithPriority (scheduler.development.js:262)
at completeRoot (react-dom.development.js:20418)
at performWorkOnRoot (react-dom.development.js:20347)
at performWork (react-dom.development.js:20255)
at requestWork (react-dom.development.js:20229)
at scheduleWork (react-dom.development.js:19912)
at dispatchAction (react-dom.development.js:13600)
at ResizeObserver.callback (web.cjs.js:71)
Okey, args must be js array, so fixed to (clj->js [1 1 1]) and no changes.
why is this react-dom? aren't you supposed to use something from react-three-fiber
to do the rendering?
react-three-fiber
is React renderer, so create args for ReactDom.render()
.
Plain Javascript example:
ReactDOM.render(
<Canvas>
<ambientLight />
<pointLight position={[10, 10, 10]} />
<Box position={[-1.2, 0, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>,
document.getElementById('root')
)
Canvas
is React component implemented by react-three-fiber
to render component to create Three.js render area(maybe).hmm ok. then I have no clue what the above error is about. I have seen it before but no clue what the cause was.
Ah, maybe the problem is just from Canvas
, not JSX tag(`mesh` and so on)...?
where do you use fn$animation-lantern
? would help to see the full code instead of guessing 😉
These are full codes, not masked any names. Pretend not to see, please. src/main/tsuguten/component/animation_lantern.cljs:
(ns tsuguten.component.animation-lantern
(:require
["react" :refer [useRef useState]]
["react-three-fiber" :refer [Canvas useFrame] :as rtf]
[reagent.core :as rc]
["three" :refer [Mesh
MeshStandardMaterial
BoxBufferGeometry]]))
(defn fn$animation-lantern
[]
(let [mesh-ref (useRef)]
(rc/as-element
[:> Canvas
])))
(defn $animation-lantern
[]
(fn []
[:> fn$animation-lantern]))
and required by
src/main/tsuguten/component/timeline.cljs:
(ns tsuguten.component.timeline
(:require
[com.rpl.specter :as sp
:refer-macros [transform setval select]]
[re-frame.core
:refer [dispatch subscribe reg-sub reg-event-fx]]
[reagent.core :as rc]
[tsuguten.component.animation-lantern
:refer [$animation-lantern]]
[tsuguten.component.floating-lantern
:refer [$floating-lantern]]
[tsuguten.component.timeline.message
:refer [$message]]
[tsuguten.util.api :as ua]
[tsuguten.util.component :as uc]
[tsuguten.util.nav :as un]))
(defn fn$timeline
[]
(let [hiccup (uc/get-component :timeline)]
(rc/as-element
(->> hiccup
(setval [(un/BY-ID "animation-lantern")
un/TAG]
$animation-lantern)
(setval [(un/BY-ID "floating-lantern")
un/TAG]
$floating-lantern)
(setval [(un/BY-ID "timeline-message")
un/TAG]
$message)))))
(defn $timeline
[]
(fn []
(let [_ @(subscribe [::uc/component :timeline])]
[:> fn$timeline])))
and required by(top level)
src/main/tsuguten/core.cljs:
(ns tsuguten.core
(:require
[clojure.browser.dom :as dom]
[re-frame.core
:refer [dispatch-sync dispatch subscribe
reg-sub]]
["react-modal" :as Modal]
["react-router-dom" :refer [Switch Route
useHistory]
:rename {BrowserRouter Router}]
[reagent.core :as rc]
[tsuguten.component.ask-load :refer [$ask-load]]
[tsuguten.component.confirm :refer [$confirm]]
[tsuguten.component.intro :refer [$intro]]
[tsuguten.component.message :refer [$message]]
[tsuguten.component.profile :refer [$profile]]
[tsuguten.component.select-actions :refer [$select-actions]]
[tsuguten.component.select-hometown :refer [$select-hometown]]
[tsuguten.component.select-lantern :refer [$select-lantern]]
[tsuguten.component.termsofuse :refer [$termsofuse]]
[tsuguten.component.throw :refer [$throw]]
(defn on-load
[]
(dispatch-sync [::events/initialize-db])
(when (:sound? @re-frame.db/app-db)
(.. (dom/get-element "audio")
play))
(.setAppElement Modal "#root")
(rc/render-component
[$root]
(dom/get-element "root")))
(defn init
[]
(js/console.info "tsuguten.core/init")
(on-load))
(defn ^:dev/before-load before-load
[]
(js/console.info "tsuguten.core/before-load")
(enable-console-print!))
(defn ^:dev/after-load after-load
[]
(js/console.info "tsuguten.core/after-load")
(on-load))
hmm yeah that needs to be a function component but isn't. I don't know what the current state of reagent is on that front
ah no wait you are calling it as a function. sorry this goes way beyond my reagent knowledge. I think the three/jsx parts are correct now. no clue about the other though
Hmm... other codes are using useHistory
as same code, so seems no problems how to use useRef
.
Yeah, I failed that I thought the problem is made by JSX blindly.
The full code is not including mesh
JSX tag and occurs same error(`lastCallbackNode is not a function`).
I need more research.
Thanks all for supporting, I will report solutions if find it.
knew I saw this error before. maybe that helps tracking it down? https://github.com/reagent-project/reagent/issues/502
Thank you, I just find the issue now. Now trying to read carefully. (maybe already you know, my English skill is very poor)
Finally I tried to them below and works fine.
• upgrade reagent
from "0.8.1"
to "0.10.0"
• Latest code:
(ns tsuguten.component.animation-lantern
(:require
["react" :refer [useRef useState]]
["react-three-fiber" :refer [Canvas useFrame] :as rtf]
[reagent.core :as rc]
["three" :refer [Mesh
MeshStandardMaterial
BoxBufferGeometry]]))
(defn fn$animation-lantern
[]
(let [mesh-ref (useRef)]
(rc/as-element
[:> Canvas
[:ambientLight]
[:pointLight {:position (clj->js [10 10 10])}]
[:mesh {:ref mesh-ref
:scale (clj->js [1 1 1])}
[:boxBufferGeometry {:attach "geometry"
:args (clj->js [1 1 1])}]
[:meshStandardMaterial {:attach "material"
:color "hotpink"}]]])))
(defn $animation-lantern
[]
(fn []
[:> fn$animation-lantern]))
$animation-lantern
can use as an element of hiccup form like this: [$animation-lantern]
This code works fine.
I hope this will help.just like in JS the names need to come from somewhere. so the JS examples likely also just import
or require
them?
Thanks for just quickly reply, @thheller. Now trying your suggest, plz wait... // I really appreciate what you've developped shadow-cljs.
Got these errors:
Uncaught Error: Assert failed: Invalid Hiccup form: [nil {:attach "geometry", :args [1 1 1]}]
boxBufferGeometory
defined in react-three-fiber/src/three-types.ts
.
Referenced code is below:
declare global {
namespace JSX {
interface IntrinsicElements {
...omit...
mesh: ReactThreeFiber.Object3DNode<THREE.Mesh, typeof THREE.Mesh>
...omit...
boxBufferGeometry: ReactThreeFiber.BufferGeometryNode<THREE.BoxBufferGeometry, typeof THREE.BoxBufferGeometry>
...omit...
}}}
In react-three-fiber
JS examples, seems no need to import/require mesh
, boxBufferGeometory
and so on.
Maybe these are not Javascript Class, sugar-syntax for JSX template.