Fork me on GitHub
#reagent
<
2023-03-27
>
Clojuri0an21:03:50

Receiving an error I don't understand:

Uncaught TypeError: (intermediate value)() is null
under init and mount

p-himik21:03:04

Nobody here will understand it either if you don't provide any context.

2
Clojuri0an21:03:53

Let me clean up the code then I'll post it here

p-himik21:03:54

Well, the first step would be to post the stacktrace.

👍 2
Clojuri0an23:03:32

Context: Stripe requires an Element class component to surround invocations that utilize the useStripe function(?) from @stripe/react-strip-js, when looking up CardElements. Otherwise, error:

Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an <Elements> provider.
    useElementsContextWithUseCase react-stripe.
The example provided at https://stripe.com/docs/connect/creating-a-payments-page?ui=elements&amp;= provides the following example, which I take to be saying that the Elements component must surround the App component that holds the checkout form. By extension and based on trial and error, I also take it to be the case that this App component holding the checkout form must be called by the REACTDom Renderer.
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';

import CheckoutForm from './CheckoutForm';

// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe(key-redacted'
);

function App() {
  return (
    <Elements stripe={stripePromise}>
      <CheckoutForm />
    </Elements>
  );
};

ReactDOM.render(<App />, document.getElementById('root'))
I have translated this into my code (excuse the idiosyncratic indentation). There are also some aspects of this that are related to my invoice payment page and not to the stripe example. I've omitted components that are not related to the example and that I am confident aren't effecting the page.
(ns invoice.frontend.main
  (:require [lambdaisland.fetch :as fetch]
          [promesa.core :as p]
          [reagent.core :as r]
          [reagent.dom :as rd]
          ["@stripe/react-stripe-js" :refer (Elements CardElement useStripe useElements)]
          ["@stripe/stripe-js" :refer (loadStripe)]))

          
(def stripe useStripe)
(def elements useElements)
(def stripePublicPromise (loadStripe "key-omitted"))

(defn fetch-client-secret []
  ;; fetch the promise and then get the json body out of it
  (p/promise
    (let [resp (fetch/get ""
    {:accept :json
    :content-type :json})]
      (prn (:body resp)))))
      
(defn stripe-card-input-section []
  (println "payment-goto-stripe called"),
   (let [card-element-options (:style #js {
      :base #js {
      :color "#32352d",
      :fontFamily ["Helvetica Neue", "Helvetica", "sans-serif"],
      :fontSmoothing "antialiased",
      :fontSize ["16px", "::placeholder" #js { :color "#aab7c4" } ]} :invalid #js { :color "e#aab7c4", :iconColor "fa755a" } })]
[:label "Card details card details card details card details"
[(r/adapt-react-class CardElement) {:options card-element-options}]]))


(defn stripe-checkout-form [] 
  (let [payment (p/promise ^js (.confirmCardPayment (stripe (fetch-client-secret) #js {:payment_method #js { :card (fn [] ^js (.getElement (elements CardElement)))
                 :billing_details #js {
                 :name "Payee Name"}}})))]
   
  (if (. payment -error)
     ((println "Error in paying")      
      (println (.message (.-error payment))))
     ((println "Payment did not return an error, testing for payment success.")
     (when ^js (.paymentIntent (.-status payment) "succeeded")
       (println "payment successful"))))
   
  [:div
   [:form {:on-submit (payment)}
    [stripe-card-input-section]
    [:button {:style "button-basic"};; add disabled
            "Confirm order"]]]))


(defn invoice-container-tree [name items]
  [(r/adapt-react-class Elements) {:stripe stripePublicPromise}
  [:div {:id "tree-invoice" :class ["form-window-visible"]}
   [invoice-id-and-num]
   [:f> stripe-checkout-form]
;;  [:button {:on-click #(handle-pay-submit) :disabled ["!stripe"] :class ["button-basic"]} "Confirm Order"]   
   [:p {:class ["text-basic"]}  "this is a test of the description and how it will look"]
   [:p {:class ["text-basic"]} "Current balance: " name]
   [:f> invoice-payment-make]
   [:button {:class ["button-basic" "p-3"] :on-click #(swap! is-show-form not)} "View past payments"]
   (if @is-show-form
     [invoice-payment-previous]
     (println "form not shown"))
   (when (and @is-show-form @is-show-p1)
     (println "testing if id was         pressed")
        (for [item items] [payment-entry]))]])


(defn mountit []
  (rd/render [invoice-container-tree "Example-Project" "invoice-container-tree" (range 3)]
    (.-body js/document)))

(defn init []
  (mountit))

(defn ^:dev/after-load start []
  (mountit))  
to reiterate the pain point, if
[(r/adapt-react-class Elements) {:stripe stripePublicPromise} ... ]
is removed from invoice-container-tree, error
Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an <Elements> provider.
    useElementsContextWithUseCase react-stripe.
appears

Clojuri0an23:03:11

The stacktrace error with the code as written is

Uncaught TypeError: (intermediate value)() is null
    invoice$frontend$main$stripe_checkout_form invoice.frontend.main.js:39
    cljs$core$IFn$_invoke$arity$2 core.cljs:3935
    cljs$core$IFn$_invoke$arity$2 core.cljs:3971
    reagent$impl$component$functional_wrap_render component.cljs:381
    reagent$impl$component$functional_do_render component.cljs:394
    functional_render component.cljs:447
    reagent$ratom$in_context ratom.cljs:44
    reagent$ratom$deref_capture ratom.cljs:57
    reagent$ratom$run_in_reaction ratom.cljs:539
    reagent$impl$component$functional_render component.cljs:445
    f component.cljs:473
    React 15
    reagent$dom$render_comp dom.cljs:17
    cljs$core$IFn$_invoke$arity$3 dom.cljs:51
    cljs$core$IFn$_invoke$arity$2 dom.cljs:37
    invoice$frontend$main$mountit main.cljs:103
    invoice$frontend$main$init main.cljs:107
    <anonymous> shadow.module.invoice-frontend.append.js:4
    globalEval invoice-frontend.js:430
    evalLoad invoice-frontend.js:1387
    <anonymous> invoice-frontend.js:1559
invoice-frontend.js line 430 > eval:39:3
    invoice$frontend$main$stripe_checkout_form invoice.frontend.main.js:39
    cljs$core$IFn$_invoke$arity$2 core.cljs:3935
    cljs$core$IFn$_invoke$arity$2 core.cljs:3971
    reagent$impl$component$functional_wrap_render component.cljs:381
    reagent$impl$component$functional_do_render component.cljs:394
    functional_render component.cljs:447
    reagent$ratom$in_context ratom.cljs:44
    reagent$ratom$deref_capture ratom.cljs:57
    reagent$ratom$run_in_reaction ratom.cljs:539
    reagent$impl$component$functional_render component.cljs:445
    f component.cljs:473
    React 10
    performSyncWorkOnRoot self-hosted:1406
    React 5
    reagent$dom$render_comp dom.cljs:17
    cljs$core$IFn$_invoke$arity$3 dom.cljs:51
    cljs$core$IFn$_invoke$arity$2 dom.cljs:37
    invoice$frontend$main$mountit main.cljs:103
    invoice$frontend$main$init main.cljs:107
    <anonymous> shadow.module.invoice-frontend.append.js:4
    globalEval invoice-frontend.js:430
    evalLoad invoice-frontend.js:1387
    <anonymous> invoice-frontend.js:1559
pointing at the following main.cljs locations:
line 103: (defn mountit []  (rd/render [invoice-container-tree "Example-Project" "invoice-container-tree" (range 3)]) 
line 107: 
(defn init []
  (mountit))

Clojuri0an23:03:59

two other error stacktraces appear, with the same general gist to them. The second

The above error occurred in the <invoice.frontend.main.stripe_checkout_form> component:

f@http://localhost:9010/js/cljs-runtime/reagent.impl.component.js:738:31
div
Elements@http://localhost:9010/js/cljs-runtime/module$node_modules$$stripe$react_stripe_js$dist$react_stripe_umd.js:13:486
cmp@http://localhost:9010/js/cljs-runtime/reagent.impl.component.js:499:43

Consider adding an error boundary to your tree to customize error handling behavior.
Visit  to learn more about error boundaries. react-dom.development.js:18688:6
    React 14
        logCapturedError
        callback
        commitUpdateQueue
        commitLayoutMountEffects_complete
        commitLayoutEffects_begin
        commitLayoutEffects
        commitRootImpl
        commitRoot
        performSyncWorkOnRoot
        flushSyncCallbacks
        flushSync
        legacyCreateRootFromDOMContainer
        legacyRenderSubtreeIntoContainer
        render
    reagent$dom$render_comp dom.cljs:17
    cljs$core$IFn$_invoke$arity$3 dom.cljs:51
    cljs$core$IFn$_invoke$arity$2 dom.cljs:37
    invoice$frontend$main$mountit main.cljs:103
    invoice$frontend$main$init main.cljs:107
    <anonymous> shadow.module.invoice-frontend.append.js:4
    globalEval invoice-frontend.js:430
    evalLoad invoice-frontend.js:1387
    <anonymous> invoice-frontend.js:1559
and the third
An error occurred when calling (invoice.frontend.main/init) shadow.module.invoice-frontend.append.js:4:59
    <anonymous> 
    globalEval 
    evalLoad 
    <anonymous> 
To clarify, these errors are appearing in the javascript console in firefox developer tools. shadow-cljs compilation does not return any errors from npx command line

Clojuri0an23:03:45

I think I fixed it. The issue was defining stripe and elements as def variables at the top, which was incorrect because usestripe need to be bound within the elements component. I deleted the global defs for useStripe and useElements and defined them with a let inside of stripe-card-input-section. For some reason I still receive an error that stripe is null, but that is a bit more of a coherent error

Clojuri0an01:03:50

I'm guessing the problem is that stripe is being called before stripe.js is fully loaded (I believe for security it is fetched from their servers), since the stripe docs make a note to check for this. I believe I need to implement some checking functionality, but am not sure how to do this within a let.

p-himik08:03:46

I would simply use Stripe via NPM, without any extra <script> tags.

Jakub Šťastný22:03:20

Hey guys. I'm looking for a recommendation for a router for me Reagent app. In React I grew rather fond of https://github.com/Paratron/hookrouter#a-quick-example, something as simple as that would be greatly preferred, but generally I'm looking for something that works well in Reagent and is popular in the community/well supported. I route with history rewrites, none of that #fragment non-sense, so that's definitely a necessity, but these days that's supported everywhere I expect. I did some basic research and found that people use reitit, which makes a lot of sense and I like reusing libraries on the back-end/front-end, but I thought I'd have asked here so I can get the full context as I'm really new to this (I've been writing CLJS, but this is day #1 with Reagent). I'm obviously trying to keep the bundle size reasonably low as well.

hifumi12322:03:02

I can 100% recommend reitit because it is completely data driven and integrates very nicely with re-frame. I’d expect integrations with vanilla reagent to be even more straightforward since you are in full control over state management.

hifumi12322:03:26

The biggest benefit I see in reitit is that you can re-use the same principles and route data across frontend and backend. More importantly, the library is extremely well documented and has tons of example code on GitHub for exactly your use cases (e.g. routing with reagent, routing with authorization, routing with re-frame, and so on)

Jakub Šťastný22:03:39

Re-frame? That's the redux for CLJS kind of thingy, innit?

Jakub Šťastný22:03:07

OK, thanks @U0479UCF48H, I'll give it a go.

hifumi12322:03:22

Correct. You do not need to use re-frame to use reitit in your frontend. I use re-frame and integrating reitit’s frontend API was straightforward. It should be even easier with pure Reagent since you are in control of everything

Jakub Šťastný22:03:10

Yeah. One step at a time 🙂 My brain's fried already.

hifumi12322:03:43

in that case I recommend starting with the absolute basics of reitit before jumping straight to integrating with reagent

hifumi12322:03:35

You can ignore the “Generating routes” section. All of the example in this section works in a CLJS REPL

hifumi12322:03:51

After you’ve read that section, you can check out the frontend docs I sent you then the example code

Jakub Šťastný00:03:52

I can see why you like it @U0479UCF48H Beyond being data driven with good API, I appreciate the docs!

👍 2