System:
Goal: to when clicking on submit on the modal, the stripe should work. According to the documentation on stripe: https://stripe.com/docs/recipes/elements-react The injectStripe function wraps the component, creating a new component with an injected stripe prop, which contains a Stripe object. You must use the wrapped component in your application instead of the original CheckoutForm.
So I assume that when I wrap it I can naturally access the stripe object within the CheckoutForm.
Error: can't read props of undefined
Then I have another error: index.js:1375 Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().
I have no idea what caused the above error.
Code:
import React, {useState, useEffect, Fragment } from 'react'
import { Container, Box, Button, Heading, Text, TextField, Modal, Spinner} from 'gestalt'
import {Elements, StripeProvider, CardElement, injectStripe} from 'react-stripe-elements'
import axios from 'axios'
import { withRouter } from "react-router-dom"
import ToastMessage from './ToastMessage'
import { getCart, calculatePrice, clearCart, calculateAmount} from './utils'
const apiUrl = process.env.API_URL || 'http://localhost:1337/'
function _CheckoutForm ({ history}) {
const [formData, setFormData] = useState({
address: '',
zipcode: '',
city: '',
confirmEmail: '',
toast: false,
toastMsg: '',
orderProcessing: false,
modal: false
})
const [cartItems,setCartItems] = useState([])
useEffect(()=> {
const items = getCart()
setCartItems(items)
}, [])
const { address, zipcode, city, confirmEmail, toast, toastMsg, orderProcessing, modal } = formData
const handleChange = ({event}) => {
setFormData({...formData, [event.target.name]: event.target.value})
}
const showToast = (toastMsg, redirect = false) => {
setFormData({...formData, toast: true, toastMsg})
setTimeout(() => setFormData({...formData, toast: false, toastMsg:''},
// if true passed to 'redirect' argument, redirect home
()=> redirect && history.push('/')),
5000)
}
const isFormEmpty = (address, zipcode, city, confirmEmail) => {
return !address || !zipcode || !city || !confirmEmail
}
const closeModal = ( )=> setFormData({...formData, modal: false})
const handleConfirmOrder = event => {
event.preventDefault()
if (isFormEmpty(address, zipcode, city, confirmEmail)){
showToast('Please fill in all the fields')
return
}
setFormData({...formData, modal:true })
}
const handleSubmitOrder = async () => {
const amount = calculateAmount(cartItems)
setFormData({...formData, orderProcessing: true})
let token
try {
//create stripe token
//create order with strapi (request to backend)
//set orderProcessing to false set modal to false
//clear user cart
//show success toast
const response = await this.props.stripe.createToken()
token = response.token.id
console.log(token)
await axios
.post(`${apiUrl}/orders`, {
amount,
productItems: cartItems,
city,
zipcode,
address,
token
})
setFormData({...formData, orderProcessing: false, modal: false})
clearCart()
showToast('Your order has been successfully submitted', true)
} catch (error) {
//set order processing to false, modal to false
//show error message in toast
setFormData({...formData, orderProcessing: false, modal: false})
showToast(error.message)
}
}
return (
<div>
some mark up code
</div>
)
}
const ConfirmationModal = ({ orderProcessing, cartItems, closeModal, handleSubmitOrder}) => (
<Modal
accessibilityCloseLabel="close"
accessibilityModalLabel="confirm your order"
heading="Confirm Your Order"
onDismiss={closeModal}
footer={
<Box display="flex" marginRight={-1} marginLeft={-1} justifyContent="center">
<Box padding={1}>
<Button
size="lg"
color="blue"
text="Submit"
disabled={orderProcessing}
onClick={handleSubmitOrder}
/>
</Box>
<Box padding={1}>
<Button
size="lg"
text="Cancel"
disabled={orderProcessing}
onClick={closeModal}
/>
</Box>
</Box>
}
role="alertdialog"
size="sm"
>
{/* order summary */}
{!orderProcessing && (
<Box display="flex" justifyContent="center" alignItems="center" direction="column" padding={2} color="lightWash">
{cartItems.map( item => (
<Box key={item._id} padding={1}>
<Text size="lg" color="blue">
{item.name} x {item.quantity} - $ {item.quantity * item.price}
</Text>
</Box>
))}
<Box padding={2}>
<Text size="lg" color="blue" bold>
Total: {calculatePrice(cartItems)}
</Text>
</Box>
</Box>
)}
{/* order processing spinner */}
<Spinner show={orderProcessing} accessibilityLabel="Order Processing Spinner" />
{orderProcessing && <Text align="center" italic>Submitting your order...</Text> }
</Modal>
)
const CheckoutForm = withRouter(injectStripe(_CheckoutForm))
const Checkout = () => (
<StripeProvider apiKey="PUBLIC_KEY">
<Elements>
<CheckoutForm />
</Elements>
</StripeProvider>
)
export default Checkout
Question: why can't I access this.props.stripe.createToken() in the CheckoutForm? How could I fix it?
I finally figured out somehow: that I need to pass props in the function _CheckoutForm, then I can just write props.history,or props.stripe.createToken().