React Native集成Stripe报错:无法找到Elements上下文
React Native集成Stripe报错:找不到Elements上下文
我要给React Native应用集成Stripe,这个应用是已完成Stripe集成且运行正常的桌面端应用的移动端版本,后端已经实现。原本想复用React Web应用里的上下文,但发现@stripe/stripe-react-native没有useElement,于是改用@stripe/stripe-js。现在一直报错:
Could not find Elements context; You need to wrap the part of your app that calls useElements() in an
provider.
但我已经用<Elements>组件包裹了表单,以下是我的代码:
checkoutForm.js
import { useNavigation } from '@react-navigation/native'; import React, { useState, useEffect, useRef } from 'react' import { View, Text, SafeAreaView, StatusBar, StyleSheet, TouchableOpacity, ScrollView, Image, Pressable , TextInput, Alert} from 'react-native'; import Icon from '../components' import { COLORS } from '../constants'; import { useStateContext } from '../context/StateContext'; import { ProductCarousel } from '../../components'; import { useElements, Elements} from "@stripe/react-stripe-js" import { CardField, useConfirmPayment, useStripe, } from '@stripe/stripe-react-native'; import { Button } from 'react-native-elements'; const CheckoutForm = () => { const navigation = useNavigation(); const [isDeliveryAddressOpen, setIsDeliveryAddressOpen] = useState(false); const [isContactNumberOpen, setIsContactNumberOpen] = useState(false); const [isDeliveryInstructionsOpen, setIsDeliveryInstructionsOpen] = useState(false); const [isCartItemsOpen, setIsCartItemsOpen] = useState(false); const [isPaymentInfoOpen, setIsPaymentInfoOpen] = useState(false); //const { confirmPayment, loading } = useConfirmPayment(); const [success, setSuccess ] = useState(false) const stripe = useStripe() const elements = useElements() const cardElement = useRef(null); const { totalPrice, cartItems } = useStateContext(); const [fullName, setFullName] = useState(''); const [email, setEmail] = useState(''); const [address, setAddress] = useState(''); const [address2, setAddress2] = useState(''); const [state, setState] = useState(''); const [city, setCity] = useState(''); const [zipCode, setZipCode] = useState(''); const [primaryNumber, setPrimaryNumber] = useState(''); const [SecondaryNumber, setSecondaryNumber] = useState(''); const [DeliveryInstructions, setDeliveryInstructions] = useState(''); const [isProcessing, setIsProcessing] = useState(false); const [isError, setIsError] = useState(false); //const [cardDetails, setCardDetails] = useState('') const toggleDeliveryAddress = () => { setIsDeliveryAddressOpen(!isDeliveryAddressOpen); }; const toggleContactNumber = () => { setIsContactNumberOpen(!isContactNumberOpen); }; const toggleDeliveryInstructions = () => { setIsDeliveryInstructionsOpen(!isDeliveryInstructionsOpen); }; const toggleCartItems = () => { setIsCartItemsOpen(!isCartItemsOpen); }; const togglePaymentInfo = () => { setIsPaymentInfoOpen(!isPaymentInfoOpen); }; //const navigate = useNavigate(); const carts = { status: 'pending', items: (cartItems), address, fullName, zipCode, state, city, DeliveryInstructions, primaryNumber, SecondaryNumber, totalPrice } const handleCheckout = async () => { const response = await fetch('/create_cart', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(carts), }); if(response.statusCode === 500) return; // eslint-disable-next-line const data = await response.json(); console.log('presseddddd', data) } /*========================================================*/ const handleSubmit = async () => { const {error, paymentMethod} = await stripe.createPaymentMethod({ type: "card", card: elements.getElement(CardField), billing_details: { name: fullName, phone: primaryNumber, email: email, address: { city: city, line1: address, state: state, postal_code: zipCode } }, }) if(!error) { try { const {id} = paymentMethod const carts = { id, amount: totalPrice, confirm: true, currency: 'CAD' } setIsProcessing(true); const response = await fetch('/create-payment-intent', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(carts), }); if(response.status === 200) { console.log(response.status) console.log("Successful payment") setSuccess(true) } const data = await response.json(); if(response.status >= 400) { console.log(response.status) console.log(data.error.split(':').pop()) setIsError(data.error) } } catch (error) { console.log("Error", error) } } else { console.log(error.message) } setIsProcessing(false) //navigate('/'); } return ( <View style={styles.container}> <View style={styles.insideContainer}> <TouchableOpacity style={styles.titleContainer} onPress={togglePaymentInfo}> <Text style={styles.title}>Payment Info</Text> {!isPaymentInfoOpen ? <Icon icon ='add-outline' color='#000' size={20}/> : <Icon icon ='remove-outline' color='#000' size={20}/> } </TouchableOpacity> {isPaymentInfoOpen && ( <View style={styles.containercollapsed}> <View style={{width: '98%'}}> <CardField ref={cardElement} postalCodeEnabled={false} placeholders={{ number: '4242 4242 4242 4242', }} cardStyle={{ backgroundColor: '#FFFFFF', textColor: '#000000', borderColor: COLORS.lightGray2, borderWidth: 1, borderRadius: 4 }} style={{ width: '100%', height: 50, }} onCardChange={(cardDetails) => { }} onFocus={(focusedField) => { }} /> </View> </View> )} </View> <View style={styles.PaybuttonView}> <Button title="Pay" onPress={ () => handleSubmit()} /*disabled={loading} loading={loading}*/ /> {/*<Pressable style={styles.Paybutton} onPress={() => {handleCheckout(); handlePayPress()} }> <Text style={{textAlign: 'center', fontSize: 20, color: '#FFFF', textTransform: 'uppercase'}}>Checkout</Text> </Pressable>*/} </View> </View> ) } export default CheckoutForm
StripeForm.js
import React from 'react' import {Elements} from "@stripe/react-stripe-js" import CheckoutForm from './CheckoutForm'; import { loadStripe } from "@stripe/stripe-js" const stripePromise = loadStripe('这里是我的密钥,我暂时去掉了'); export const StipeForm = () => { return ( <Elements stripe={stripePromise}> <CheckoutForm /> </Elements> ) }
问题原因及修复方案
核心错误
你混用了Web端和React Native端的Stripe SDK,两者上下文体系不兼容:
@stripe/stripe-react-native是专为React Native设计的独立SDK,有自己的上下文管理逻辑- 你同时导入了Web端的
useElements/Elements和Native端的CardField/useStripe,导致Web端的Elements无法为Native端的钩子提供正确上下文
修复步骤
- 清理依赖:卸载
@stripe/stripe-js和@stripe/react-stripe-js,只保留@stripe/stripe-react-native - 替换上下文提供者:用Native SDK的
StripeProvider包裹整个应用(而非Web端的Elements)// 根组件示例(如App.js) import { StripeProvider } from '@stripe/stripe-react-native'; export default function App() { return ( <StripeProvider publishableKey="你的Stripe公钥" merchantIdentifier="merchant.com.yourappname" // Apple Pay配置,可选 > {/* 你的导航或应用内容 */} <StripeForm /> </StripeProvider> ); } - 调整StripeForm组件:移除Web端的
Elements,直接渲染CheckoutFormimport React from 'react' import CheckoutForm from './CheckoutForm'; export const StripeForm = () => { return <CheckoutForm />; } - 修改CheckoutForm逻辑:移除Web端导入,使用Native SDK的API
// 移除这行:import { useElements, Elements} from "@stripe/react-stripe-js" import { CardField, useStripe } from '@stripe/stripe-react-native'; const CheckoutForm = () => { // 移除这行:const elements = useElements() const handleSubmit = async () => { const stripe = useStripe(); // Native SDK无需通过elements获取CardField,直接调用createPaymentMethod即可读取卡片数据 const { error, paymentMethod } = await stripe.createPaymentMethod({ type: 'card', billing_details: { name: fullName, phone: primaryNumber, email: email, address: { city: city, line1: address, state: state, postal_code: zipCode } }, }); // 后续支付流程保持不变 } }
额外注意
- 后端Payment Intent逻辑无需修改,和Web端兼容
- 测试使用Stripe官方测试卡号:
4242 4242 4242 4242
内容的提问来源于stack exchange,提问作者Ons Jannet
相关产品推荐
相关产品推荐

