New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anyone managed to get code-splitting with React working? #186

Open
DjebbZ opened this Issue Feb 7, 2019 · 6 comments

Comments

Projects
None yet
2 participants
@DjebbZ
Copy link
Contributor

DjebbZ commented Feb 7, 2019

Hello,

I'm trying to integrate React.lazy and React.Suspense from React 16.6.0 with Rum in ClojureScript with code-splitting, and I'm failing hard. Basically I'm trying to do something like (everything is inlined for simplicity):

(rum/defc some-component
          []
          (js/React.createElement js/React.Suspense
                                  #js {:fallback (sablono.core/html [:h3 "PLACEHOLDER"])}
                                  (js/React.createElement (js/React.lazy (fn []
                                                                           (js/Promise. (fn [resolve-p reject-p]
                                                                                          (loader/load :popin
                                                                                                       (fn []
                                                                                                         (let [component (resolve 'yoda.components.popin-content/dummy)]
                                                                                                           (resolve-p (component))))))))))))

I've put many console.log and it clearly shows that the problem comes from where I resolve the promise (resolve-p (component)). All my attempts (which are variations of the code above) resulted in React telling me in the console that

"Object is not valid React child"

or

"Warning: lazy: Expected the result of a dynamic import() call. Instead received: #'my-
app.components/dummy (Note: it's the Var of the component)
Your code should look like:
const MyComponent = lazy(() => import('./MyComponent'))

or

"Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Object]
Your code should look like:
const MyComponent = lazy(() => import('./MyComponent'))

I've tried so many combinations of functions, rum/defc, (js/React.createElement ...) that I've lost count. Does someone here managed to get something working? Working solutions very appreciated.

@DjebbZ

This comment has been minimized.

Copy link
Contributor Author

DjebbZ commented Feb 7, 2019

I got a little further: now that last expression is (resolve-p #js {"default" component}). And I get a new error message:

react-dom.inc.js:49 Uncaught Invariant Violation: Element type is invalid. Received a promise that resolves to: #'yoda.components.popin-content/dummy. Promise elements must resolve to a class or function.

I think my problem boils down to this: given a Var that I get with (cljs.core/resolve 'fully.qualified/symbol), how can I retrieve the corresponding React Component ? React complains that I don't resolve the promise with a valid React component.

@tonsky

This comment has been minimized.

Copy link
Owner

tonsky commented Feb 7, 2019

try (:rum/class (meta ctor)) where ctor is the function stored in component defined with defc

@DjebbZ

This comment has been minimized.

Copy link
Contributor Author

DjebbZ commented Feb 8, 2019

It almost works. It happens that (cljs.core/resolve 'namespace/component-symbol) returns a Var, so you have to deref it to get the underlying constructor: (:rum/class (meta @component)).

Thanks a lot!

I suppose it would make a good addition to the interop doc.

@DjebbZ DjebbZ closed this Feb 8, 2019

@DjebbZ

This comment has been minimized.

Copy link
Contributor Author

DjebbZ commented Feb 8, 2019

Sorry to reopen so quickly but now I have another problem. Given a constructor as done above, how does one call it inside rum ? My problem is that for la lazy-loaded component that takes no argument, it works fine. But the lazy loaded component I really want to use takes argument, I can't find a way to pass arguments to the lazy loaded component.

Here's my example:

(let [component (-> (resolve 'my-app.components/dummy)
                    (deref)
                    (meta)
                    (:rum/class)
                    #_(partial arg1 arg2))]
  (resolve-p #js {"default" component}))

If I make dummy take no argument, works perfectly. I tried making a partial, doesn't work. I also tried after reading the source code passing arguments the "rum" way, like this (full example, real-world code, just inlined):

(rum/defc render-popin-placeholder < rum/static
          [arg1 arg2]
          (js/console.log "popin placeholder")
          (js/React.createElement js/React.Suspense
                                  #js {:fallback (sablono.core/html [:h3 "PLACEHOLDER"])}
                                  (js/React.createElement (js/React.lazy (fn []
                                                                           (js/console.log "INSIDE React.lazy")
                                                                           (js/Promise. (fn [resolve-p reject-p]
                                                                                          (loader/load :popin
                                                                                                       (fn []
                                                                                                         (js/console.log ":popin module loaded")
                                                                                                         (let [component (-> (resolve 'my-app.components/real-popin)
                                                                                                                             (deref)
                                                                                                                             (meta)
                                                                                                                             (:rum/class)
                                                                                                                             #_(partial reconciler popin-state))]
                                                                                                           (js/console.log "popin component" component)
                                                                                                           (resolve-p #js {"default" component}))))))))
                                                          #js {":rum/initial-state" {:rum/args [arg1 arg2]}} ;; nope
                                                          ; #js {":rum/args" [reconciler popin-state]} ;; also tried this
                                                          )))

When I console.log the arguments inside real-popin, I get undefined.

Again, help really appreciated. I realize I'm doing some "edgy" interop between rum and the new features in React.

@DjebbZ DjebbZ reopened this Feb 8, 2019

@tonsky

This comment has been minimized.

Copy link
Owner

tonsky commented Feb 9, 2019

Something like this:

(js/React.createElement class #js { ":rum/initial-state" { :rum/args args }})
@DjebbZ

This comment has been minimized.

Copy link
Contributor Author

DjebbZ commented Feb 13, 2019

I tried this too, didn't work (not sure I understand why since this is what rum does...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment