I’m trying and failing when using the nivo ResponsiveBar component. The nivo Bar component works. So I’m discounting my basic library import etc. as possible problems. For reference, the component definitions are linked below.
And the Bar/ResponsiveBar component is rendered with the code below. In this code if I change ResponsiveBar to Bar I get the browser showing the expected bar chart.
But with ResponsiveBar I get this error. I have truncated the stacktrace for the sake of brevity.
react-dom-client.development.js:4261 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. at createFiberFromTypeAndProps (react-dom-client.development.js:4261:28) at createFiberFromElement (react-dom-client.development.js:4275:14) at reconcileChildFibersImpl (react-dom-client.development.js:7881:31) at eval (react-dom-client.development.js:8059:33) at reconcileChildren (react-dom-client.development.js:8623:13) at updateFunctionComponent (react-dom-client.development.js:8916:7) at beginWork (react-dom-client.development.js:10524:18) at runWithFiberInDEV (react-dom-client.development.js:1521:30) at performUnitOfWork (react-dom-client.development.js:15134:22) at renderRootSync (react-dom-client.development.js:14958:41) createFiberFromTypeAndProps @ react-dom-client.development.js:4261 createFiberFromElement @ react-dom-client.development.js:4275 reconcileChildFibersImpl @ react-dom-client.development.js:7881 eval @ react-dom-client.development.js:8059 reconcileChildren @ react-dom-client.development.js:8623 updateFunctionComponent @ react-dom-client.development.js:8916 beginWork @ react-dom-client.development.js:10524 runWithFiberInDEV @ react-dom-client.development.js:1521 performUnitOfWork @ react-dom-client.development.js:15134 renderRootSync @ react-dom-client.development.js:14958 performWorkOnRoot @ react-dom-client.development.js:14464 performWorkOnRootViaSchedulerTask @ react-dom-client.development.js:16218 performWorkUntilDeadline @ scheduler.development.js:46 <…>
I’m picking up clojure after a several year break, am new to clojurescript and javascript. So I hope I’m not asking something that has a simple and obvious answer. Any thoughts on why I’m getting an error with ResponsiveBar and not Bar?
This is the relevant part of the error message. It basically means that ResponsiveBar is not a react component, but likely an object with a property holding that component. So, the relevant bit that is missing from your snippet is where this is coming from. I’m guessing its part of your ns:require structure and just translated incorrectly.
FWIW it is almost never useful to look at the source of JS components, as that often doesn’t have much to do with how its actually used. From the docs I found
import { ResponsiveBar } from '@nivo/bar'
which would translate to
(ns your.thing
(:require ["@nivo/bar" :refer (ResponsiveBar)]))
;; and then used like you had
[:> ResponsiveBar ...]
You can find many examples of how things are translated in the docs. I’m not exactly sure how reagent translates the :data or :margin (i.e. nested maps). But should be fine.
You said changing it to Bar works, so same question there. Where does that come from? It should work the same way.
Another way to debug this is just quickly firing up a REPL (e.g. npx shadow-cljs browser-repl) and doing a (require ‘[“@nivo/bar” :as x]) and then (js/console.dir x)and looking at the Object in the browser devtools. It should be an object with a ResponsiveBar property (which likely is a function). (js/console.dir (.-ResponsiveBar x)) would let you inspect that thing alone. The REPL output alone unfortunately isn’t all that useful often for these kinds of objects, hence using js/console.dir and looking at it in the console.
Appreciate the response and guidance. Here’s the entirety of my application so far. As you can see I’m importing Bar and ResponsiveBar the same way. And Bar and ResponsiveBar are used in the exact same way in code, only one works and the other does not.
I tried the repl as you suggested, and I don’t see any difference between these objects. I guess my next step would be to cut out cljs and see if I can get these components working in JS. Is there a way to look at the JS that’s being generated for the cljs code? I tried using the browser debugger but the sources in there preserve the cljs code.
It is one of those fun commonjs → ESM transition issues. It isn’t directly the @nivo/bar dependency but rather it using the react-virtualized-autosizer component. That one ships a commonjs variant and a ESM variant, which are used differently depending on which is referenced. The nivo thing uses it and expecting the ESM variant, but shadow-cljs still defaults to picking the commonjs variant. Hence you get a misleading error where the actual problem is way further down and not related to your code.
You can configure shadow-cljs to prefer the ESM files instead and that seems to fix the issue. Just set
in your build config. The default is ["browser" "require" "default" "module" "import"], so this is just basically re-ordering what is picked first and module/import here referring to ESM files generally.
Unfortunately this may lead to problems with other packages, because the opposite scenario where something may expect the commonjs code is also quite common. It may also just work fine forever, all depends on the mix of packages you use.
I don’t plan on building a large complex application, so hopefully I won’t run into the scenario you’ve described. If I understand the problem correctly, there are still many packages that are shipped as CJS, and shadow-cljs prefers CJS. I wonder if there’s a way to make this issue more apparent? I would never have figured this out on my own.
On an unrelated note, :proxy-predicate is not mentioned in the :shadow-cljs user guide at Shadow CLJS User’s Guide This was necessary for working with a non-clojure backend server. I could not work out where the documentation source resides, otherwise I would have contributed a PR.
I generally recommend that people do not use proxy at all. You can just have your server on localhost:5000 serve the static .js files. Unless for whatever reason that server can’t do that of course, then proxy is fine.
If I understand correctly your recommendation then would be to compile the pages to static js using clojurescript, and then use my python server to serve up those js files? That makes sense. I’ll have to work out the specific workflow for that scenario. I might lose the live reloading that I get with shadow.
The :clientbuild stays as it. It outputs a public/js/main.js file and :dev-http is serving that file via localhost:8080. So, instead you just remove :dev-http entirely and either have your python server serve the public directly as static files, or change :output-dir in the build config to something other than ”public/js”.
The workflow really doesn’t change, you just remove one extra server. Live reloading is also not affected at all as :dev-http is not the thing doing it. So it doesn’t matter at all if you use a python server or :dev-http as far as that is concerned.