reagent

Clojuri0an 2023-03-27T21:28:50.370549Z

Receiving an error I don't understand:

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

p-himik 2023-03-27T21:32:04.720339Z

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

➕ 1
Clojuri0an 2023-03-27T21:37:53.578359Z

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

p-himik 2023-03-27T21:48:54.620729Z

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

👍 1
Clojuri0an 2023-03-27T23:06:32.921919Z

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

Clojuri0an 2023-03-27T23:18:11.429879Z

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))

Clojuri0an 2023-03-27T23:20:59.681179Z

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

Clojuri0an 2023-03-27T23:55:45.163379Z

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

Clojuri0an 2023-03-28T01:33:50.081269Z

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-himik 2023-03-28T08:49:46.943539Z

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

2023-03-27T22:05:20.969969Z

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.

hifumi123 2023-03-27T22:29:02.971499Z

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.

hifumi123 2023-03-27T22:30:26.770699Z

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)

2023-03-27T22:31:39.754529Z

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

2023-03-27T22:32:07.671759Z

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

hifumi123 2023-03-27T22:32:22.747949Z

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

2023-03-27T22:33:10.105589Z

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

hifumi123 2023-03-27T22:33:43.378069Z

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

hifumi123 2023-03-27T22:33:54.762309Z

These docs introduce how the routing syntax works https://cljdoc.org/d/metosin/reitit/0.6.0/doc/basics/route-syntax

hifumi123 2023-03-27T22:34:35.456279Z

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

hifumi123 2023-03-27T22:37:51.569869Z

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

2023-03-28T00:27:52.375999Z

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

👍 1