shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
erwinrooijakkers 2021-06-24T08:31:31.256200Z

I have a JSX component that returns a React component:

import React from "react";
import Select from "react-select";
import Survey from "survey-react";

const CustomSearchWidget = ({ choices, onChange }) => {
...
  return (
    <Select
      ...
    />
  );
};

export const SearchWidget = {
  ...
  render: (question) => {
    return (
      <CustomSearchWidget
        ...
      />
    );
  },
};
When I compile this using babel with npx babel *.jsx --out-dir gen and import this compiled file in a clojurescript file like so:
["./surveyjs/widgets/gen/SearchWidget.js" :refer [SearchWidget]]
and use the SearchWidget in the application I get this error:
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object
If I return <div/> instead of <Select/> this error does not occur

erwinrooijakkers 2021-06-24T08:31:38.256400Z

Is there a workaround for this?

thheller 2021-06-24T08:45:16.257400Z

@erwinrooijakkers you ... out a bunch of code so I cannot see what this SearchWidget actually is. from what you left in it is not a correct react component and that is what the error is telling you?

thheller 2021-06-24T08:45:36.257800Z

maybe you just forgot a export const SearchWidget = class extends React.Component {?

erwinrooijakkers 2021-06-24T09:05:24.258Z

Full code:

import React from "react";
import Select from "react-select";
import Survey from "survey-react";

const CustomSearchWidget = ({ choices, onChange }) => {
  const handleChange = (option) => {
    onChange(option.value);
  };

  const options = choices.map((choice) => {
    return { label: choice, value: choice };
  });

  return (
    <Select
      className="basic-single"
      classNamePrefix="select"
      isClearable={true}
      isSearchable={true}
      name="color"
      options={options}
      onChange={handleChange}
    />
  );
};

export const SearchWidget = {
  name: "search dropdown",
  title: "Dropdown with search",
  isFit: function (question) {
    return question.getType() === "searchdropdown";
  },
  activatedByChanged: function (activatedBy) {
    Survey.JsonObject.metaData.addClass("searchdropdown", [], null, "text");
    Survey.JsonObject.metaData.addProperties("searchdropdown", [
      { name: "choices", default: ["Click Me"] },
    ]);
  },
  render: (question) => {
    return (
      <CustomSearchWidget
        choices={question.choices}
        placeholder={question.title}
        onChange={(val) => (question.value = val)}
      />
    );
  },
};

erwinrooijakkers 2021-06-24T09:05:36.258500Z

Thank you. I added the full code in the reply

erwinrooijakkers 2021-06-24T09:08:42.258800Z

export const SearchWidget = class extends React.Component { gives babel compilation error:

Unexpected token (28:6)

  26 |
  27 | export const SearchWidget =  class extends React.Component {
> 28 |   name: "search dropdown",
     |       ^

erwinrooijakkers 2021-06-24T09:09:08.259Z

with npx babel *.jsx --out-dir gen

erwinrooijakkers 2021-06-24T09:16:23.259200Z

these are functional components I understand from the party that helped made them, so cannot add a class extends React.Component, although they say in the end it’s the same

erwinrooijakkers 2021-06-24T09:18:00.259300Z

(And it goes wrong when importing like so):

(ns nl.mediquest.questionnaire-frontend.component.survey
  (:require
   ["./surveyjs/widgets/gen/SearchWidget.js" :refer [SearchWidget]]
   [survey-react :refer [Survey StylesManager CustomWidgetCollection]]))

(.. CustomWidgetCollection -Instance (addCustomWidget SearchWidget "customtype"))

erwinrooijakkers 2021-06-24T09:18:56.259500Z

package.json

{
  "devDependencies": {
    "@babel/cli": "^7.14.5",
    "@babel/core": "^7.14.6",
    "@babel/plugin-transform-react-jsx": "^7.14.5",
    "shadow-cljs": "2.10.12"
  },
  "dependencies": {
    "@fortawesome/fontawesome-pro": "^5.13.0",
    "@fullhuman/postcss-purgecss": "^2.0.5",
    "autoprefixer": "9.8.0",
    "global": "^4.4.0",
    "postcss-cli": "7.1.1",
    "postcss-import": "^12.0.1",
    "react": "^16.13.0",
    "react-dom": "16.13.0",
    "react-select": "^4.3.1",
    "recharts": "1.8.5",
    "survey-react": "^1.8.4",
    "tailwindcss": "1.4.6"
  },
  "scripts": {
    "build-css": "node_modules/postcss-cli/bin/postcss resources/public/css/tailwind.css -o resources/public/style.css"
  }

erwinrooijakkers 2021-06-24T09:30:07.259800Z

it is something with the Select component, because when I turn it into <div/> the webpage renders and there are no errors

thheller 2021-06-24T09:37:18.260300Z

as the error is telling you an object is not a valid react component

thheller 2021-06-24T09:37:41.260800Z

so whatever it is it is invalid. it has nothing to do with shadow-cljs, it is purely a react error

thheller 2021-06-24T09:38:08.261300Z

maybe its meant to be a React.createClass({ name: ...})

erwinrooijakkers 2021-06-24T09:46:14.261600Z

I fixed it by changing:

import Select from "react-select";
to
const Select = require('react-select');

erwinrooijakkers 2021-06-24T09:46:35.261900Z

the same code did work in javascript for some reason

erwinrooijakkers 2021-06-24T09:46:42.262200Z

in the create-react-app

erwinrooijakkers 2021-06-24T09:46:45.262400Z

anyway

erwinrooijakkers 2021-06-24T09:46:49.262700Z

thanks for the help!

erwinrooijakkers 2021-06-24T09:49:11.262800Z

via https://github.com/facebook/react/issues/13445#issuecomment-595470399

Alex J Henderson 2021-06-24T10:58:59.269300Z

I'm using shadow-cljs, with the helix library. I'm noticing that a tagged js literal such as #js [] when placed anywhere within the form of a call to react/useState, breaks the hooks state preservation on hot reload. For example test-state is preserved if I make a code change with this component:

(defnc test-component []
  (let [[test-state set-test-state] (react/useState (do [] 1))]
    ($ "button" {:on-click #(set-test-state inc)} (str "test1: " test-state))))
but if I tag the array inside the do form with a #JS like the following, the state of test-state is not preserved if I make a code change:
(defnc test-component []
  (let [[test-state set-test-state] (react/useState (do #js [] 1))]
    ($ "button" {:on-click #(set-test-state inc)} (str "test1: " test-state))))
maybe this is not a shadow-cljs issue, but any help would be much appreciated, thanks!

thheller 2021-06-24T11:43:15.269600Z

why is there a do in the first place?

thheller 2021-06-24T11:43:48.270200Z

but yeah not a shadow-cljs issue. that is either helix or react, don't know

Aron 2021-06-24T11:44:00.270400Z

helik πŸ˜„

Alex J Henderson 2021-06-24T12:45:03.270600Z

simply to demonstrate that it's some sort of reader issue. The empty array never even reaches react/useState only the '1' does.

thheller 2021-06-24T12:48:38.272100Z

maybe helix does some stuff in a macro. I don't really know how hot-reload works with hooks. I would assume useState is always lost, just like local state is normally lost. given that component type changes it should go through a full unmount/mount cycle

Alex J Henderson 2021-06-24T12:50:53.272500Z

the weird thing is it seems to be related to the tagged literal. If I write

(defnc test-component []
  (let [a #js []
        [test-state set-test-state] (react/useState (do a 1))]
    ($ "button" {:on-click #(set-test-state inc)} (str "test1: " test-state))))
it works fine

Alex J Henderson 2021-06-24T12:52:14.272800Z

And this works fine also

(defnc test-component []
  (let [[test-state set-test-state] (react/useState (do (array) 1))]
    ($ "button" {:on-click #(set-test-state inc)} (str "test1: " test-state))))
that's what makes me think it's either clojurescript or shadow

thheller 2021-06-24T12:52:17.273Z

really need to ask in the #helix channel. I don't have a clue what defnc does internally

Alex J Henderson 2021-06-24T12:52:33.273200Z

ok thanks very much πŸ™‚

thheller 2021-06-24T12:54:19.273400Z

just a glance at the macro code makes me pretty certain that it is the macro

thheller 2021-06-24T12:54:27.273900Z

not shadow or cljs

Alex J Henderson 2021-06-24T12:55:34.274100Z

ah brilliant, that's very helpful, I guess it's analysing the code for calls to react hooks and something about the tagged literal is tripping it up, cheers