我是 javascript 的新手,我正在做一个项目,我需要访问 JavaScript 对象中的 prices 属性。此对象是条带集成中的产品对象。当我记录 productData.prices 时,它显示未定义,即使 productData 对象具有 prices 属性。我不确定为什么会这样。有人可以帮助我了解可能导致此问题的原因吗?
这是javascript代码
export default function Subscription() {
const [loading, setLoading] = useState(false);
const [products, setProducts] = useState([]);
const { currentUser } = useAuth();
const [stripe, setStripe] = useState(null);
useEffect(() => {
const initializeStripe = async () => {
const stripe = await loadStripe(
process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY
);
setStripe(stripe);
};
initializeStripe();
const q = query(collection(db, "products"), where("active", "==", true));
getDocs(q).then((querySnapshot) => {
const products = {};
querySnapshot.forEach(async (doc) => {
products[doc.id] = doc.data();
const priceSnapshot = await getDocs(
collection(db, "products", doc.id, "prices")
);
priceSnapshot.forEach((price) => {
products[doc.id].prices = {
priceId: price.id,
priceData: price.data(),
};
});
});
setProducts(products);
});
}, []);
async function loadCheckOut(priceId) {
setLoading(true);
const usersRef = doc(collection(db, "users"), currentUser.uid);
const checkoutSessionRef = collection(usersRef, "checkout_sessions");
const docRef = await addDoc(checkoutSessionRef, {
price: priceId,
trial_from_plan: false,
success_url: window.location.origin,
cancel_url: window.location.origin,
});
onSnapshot(docRef, (snap) => {
const { error, sessionId } = snap.data();
if (error) {
alert(`An error occurred: ${error.message}`);
}
if (sessionId && stripe) {
stripe.redirectToCheckout({ sessionId });
}
});
}
return (
<>
<Container className="mt-4 mb-4">
<h1 className="text-center mt-4">Choose Your Plan</h1>
<Row className="justify-content-center mt-4">
{Object.entries(products).map(([productId, productData]) => {
console.log(productData);
console.log(productData.prices);
return (
<Col md={4} key={productId}>
<Card>
<Card.Header className="text-center">
<h5>{productData.name}</h5>
<h5>$20.00 / month</h5>
</Card.Header>
<Card.Body>
<h6>{productData.description}</h6>
<Button
onClick={() => loadCheckOut(productData?.prices?.priceId)}
variant="primary"
block
disabled={loading}
>
{loading ? (
<>
<Spinner
animation="border"
size="sm"
className="mr-2"
/>
Loading...
</>
) : (
"Subscribe"
)}
</Button>
</Card.Body>
</Card>
</Col>
);
})}
</Row>
</Container>
</>
);
}
console.log(productData);
记录以下对象
{
"images": [],
"description": "Access to dashboard",
"tax_code": null,
"active": true,
"role": "premium",
"name": "Premium",
"metadata": {
"firebaseRole": "premium"
},
"prices": {
"priceId": "price_1N7SdbJHqW6OBlJ5itJgsGON",
"priceData": {
"billing_scheme": "per_unit",
"interval": "month",
"unit_amount": 2000,
"currency": "usd",
"product": "prod_NtF7pDBsqj5psh",
"transform_quantity": null,
"type": "recurring",
"active": true,
"metadata": {},
"tax_behavior": "unspecified",
"tiers": null,
"tiers_mode": null,
"interval_count": 1,
"recurring": {
"interval": "month",
"aggregate_usage": null,
"usage_type": "licensed",
"interval_count": 1,
"trial_period_days": null
},
"description": null,
"trial_period_days": null
}
}
}
console.log(productData.prices);
日志未定义。有人可以帮助我了解这里发生的事情以及我如何解决它吗?谢谢。
根据您提供的代码,问题似乎出在您设置
products
状态的方式上。在您的代码中,您将 products
初始化为一个空对象,然后遍历 querySnapshot
以使用 forEach
和 getDocs
异步填充它。
由于
products
的填充是异步完成的,因此有可能在异步操作完成之前设置状态。因此,当您尝试在代码的渲染部分访问 productData.prices
时,它可能仍然是未定义的。
要解决此问题,您可以修改代码以使用
Promise.all
在设置状态之前等待所有异步操作完成。这是实现此方法的代码的更新版本:
useEffect(() => {
const initializeStripe = async () => {
const stripe = await loadStripe(
process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY
);
setStripe(stripe);
};
initializeStripe();
const fetchData = async () => {
const q = query(collection(db, "products"), where("active", "==", true));
const querySnapshot = await getDocs(q);
const products = {};
const pricePromises = querySnapshot.docs.map(async (doc) => {
const priceSnapshot = await getDocs(
collection(db, "products", doc.id, "prices")
);
const prices = {};
priceSnapshot.forEach((price) => {
prices[price.id] = price.data();
});
return { id: doc.id, data: doc.data(), prices };
});
const productsData = await Promise.all(pricePromises);
productsData.forEach((productData) => {
products[productData.id] = { ...productData.data, prices: productData.prices };
});
setProducts(products);
};
fetchData();
}, []);
通过使用
Promise.all
和map
创建一个承诺数组(pricePromises
),我们可以确保在设置products
的状态之前完成所有获取价格的异步操作。然后,在productsData
数组中,每个productData
对象包含相应的prices
属性。
有了这个更新的代码,
productData.prices
在组件的渲染部分访问时不应再未定义。
根据您提供的代码 💁🏻u200d♂️ 我认为,
products
对象在控制台中的显示方式 👀:
{"key":"value"}
👨🏻u200d🏫这里的密钥以实际字符串的形式呈现,🤸🏻u200d♂️以另一种方式,
products
obj 在控制台中像 JSON
数据一样格式化,所以当你想访问它时像 products.property
它不会工作,除非你从 {"key":"value"}
解析到 {key:"value"}
使用:
JSON.parse(products);
它将把它转换成一个具有可访问属性的实际javascript对象✨👌🏻。 或者你可以使用💁🏻u200d♂️:
products["property"]
它应该工作,希望,👀