Receiving an error I don't understand:
Uncaught TypeError: (intermediate value)() is null
under init and mountNobody here will understand it either if you don't provide any context.
Let me clean up the code then I'll post it here
Well, the first step would be to post the stacktrace.
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&= 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.
appearsThe 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))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 lineI 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
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.
I would simply use Stripe via NPM, without any extra <script> tags.
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.
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.
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)
Check out the docs https://github.com/metosin/reitit/blob/master/doc/frontend/browser.md and the reagent demo here https://github.com/metosin/reitit/blob/master/examples/frontend/src/frontend/core.cljs
Re-frame? That's the redux for CLJS kind of thingy, innit?
OK, thanks @hifumi123, I'll give it a go.
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
Yeah. One step at a time 🙂 My brain's fried already.
in that case I recommend starting with the absolute basics of reitit before jumping straight to integrating with reagent
These docs introduce how the routing syntax works https://cljdoc.org/d/metosin/reitit/0.6.0/doc/basics/route-syntax
You can ignore the “Generating routes” section. All of the example in this section works in a CLJS REPL
After you’ve read that section, you can check out the frontend docs I sent you then the example code
I can see why you like it @hifumi123 Beyond being data driven with good API, I appreciate the docs!