I have a custom behavior component, which allows a Plasmic Studio user to give any button a special onClick handler. The onClick handler sends a network request and tracks an isLoading
state in the component.
How do I expose isLoading
to the Plasmic Studio user so they can use it in dynamic values. e.g. to conditionally render the button text depending on the loading state.
I have tried wrapping the custom behavior component in a <DynamicProvider>
component so that I can expose isLoading
to the button. Doing this, I can succesfully access this dynamic value in the Studio, but the custom onClick behavior is no longer triggered. I am probably doing something wrong here. Does anyone have an example of how best to implement this?
Please see my custom behaviour code component below:
import { apiClient } from "@/services/apiClient";
import { DataProvider } from "@plasmicapp/host";
import { useMutation } from "@tanstack/react-query";
import React, { ReactElement, cloneElement, useEffect } from "react";
import type {
BookingCheckoutResponse,
CreateBookingPayload,
StripeButtonProps,
} from "./types";
async function createBooking(data: CreateBookingPayload) {
const res = await apiClient.post("/bookings/checkout", data);
return res.data;
}
function StripeBookingButton({
children,
bookerId,
customers,
group_coupon,
locationId,
start,
payInFull,
onLoading,
...props
}: StripeButtonProps) {
const payload = {
booking: {
auto_duration: true,
booker: bookerId,
customers: customers,
end: null,
group_coupon: group_coupon,
location: locationId,
source: "Online",
start: start,
tos_accepted_at: new Date().toISOString(),
},
pay_in_full: payInFull,
provider: "stripe",
};
// For composability in Plasmic Studio
const filteredProps = Object.fromEntries(
Object.entries(props).filter(
([key]) => !key.startsWith("data-plasmic") && key !== "className"
)
);
const newBooking = useMutation(
(payload: CreateBookingPayload) => createBooking(payload),
{
onError: (err: any) => {
if (err.response.status === 409) {
// handleSlotNotAvailable();
console.log("Slot not available");
}
},
}
);
const handleCheckout = async () => {
console.log("Handling Checkout...");
await newBooking.mutateAsync(payload);
};
useEffect(() => {
if (newBooking.isSuccess) {
const data = newBooking.data as BookingCheckoutResponse;
if (data.amount_total === 0 || data.amount_owing === 0) {
window.location.href =
process.env.NEXT_PUBLIC_BOOKING_SUCCESS_URL || "/";
} else {
if (data.booking_id && data.booking_success) {
window.location.href = data.url;
}
}
}
}, [newBooking.data, newBooking.isSuccess]);
return (
<DataProvider name="stripeBookingButtonState" data={newBooking}>
<React.Fragment>
{React.Children.map(children, (child) =>
cloneElement(child as ReactElement, {
...filteredProps, // forward extra props for composability
onClick: handleCheckout,
})
)}
</React.Fragment>
</DataProvider>
);
}
export default StripeBookingButton;