为什么我的 child 组件会渲染多次?
Why does my child component render multiple times?
我在我的项目中使用 Stripe API,当付款成功后,我想将下的订单添加到我的 collection。当我 运行 以下代码时,它多次添加订单并且没有特定模式。它还将多个订单添加到 collection:
Success.js:
import React from "react";
import AddOrder from "../orders/AddOrder";
import { useNavigate, useParams } from "react-router-dom";
import { useMutation, useQuery } from "@apollo/client";
import queries from "../../queries";
import { Alert, Button } from "react-bootstrap";
function Success() {
const logo = require("../../assets/delivery-package.gif");
const navigate = useNavigate();
const { secret } = useParams();
const { loading, error, data } = useQuery(queries.GET_SESSION, { variables: { id: secret } });
const [deleteSession] = useMutation(queries.DELETE_SESSION);
if (loading) {
return <div>Loading...</div>;
} else if (error) {
navigate("/notfound");
} else if (data.session) {
deleteSession({
variables: {
id: secret,
},
});
return (
<div>
<AddOrder />
<Alert variant="success" style={{ fontSize: "25px" }}>
Order Placed Successfully
</Alert>
<img alt="order success" id="logo" src={logo} style={{ width: "70%", height: "70%", marginTop: "30px" }} />
<br />
<br />
<Button onClick={() => navigate("/")}>Home</Button>
</div>
);
} else {
navigate("/notfound");
}
}
export default Success;
添加订单:
import { useMutation, useQuery } from "@apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [addOrder, currentUser, data, editUser, getUserOrders, text]);
}
export default AddOrder;
只有一个return不知道是成功页面的问题还是Addorder函数的问题
在 AddOrder 组件中,您没有 returning 任何 JSX。您实际上是在组件主体中发送订单请求(在渲染期间,这非常糟糕,因为它是 side-effect)。
不要return addOrder、editUser 调用。根据您的业务逻辑将它们放在 useEffect 或事件处理程序中。
可能原因:
您的组件必须由某个父组件呈现,例如,通过调用 setState,并且当该组件是 re-rendered Success 子组件(反过来又不会 return JSX.Element) 也是 re-rendered
原因:
在你的 AddOrder
组件中有一个 useEffect
用来防止 re-renders 当传递的参数没有改变时,其中有一个 "new Date().getTime()",我相信这是一个useEffect
总是 return 为真并请求 re-render 的主要原因。不仅如此,还有:
new Date().toString 作为 useEffect
的参数
将函数(addOrder、editOrder)传递给 useEffect
重新渲染时可能总是新函数的参数。
currentUser
可能是每个渲染的新对象,幸运的是你只需要 userid,它又是一个原始类型,并且无论 re-rendering.
解决方案:
addOrder
或 editOrder
函数是否发生了变化,无需分析,因为它们只是函数,不要添加任何具体的 "value",例如,如果您的意图是重新渲染(为了能够添加)只有当项目发生变化时,你可以只留下 getUserOrders.data
和 data
(useQuery(queries.GET_USER_BY_ID)...)
新代码:
import { useMutation, useQuery } from "@apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [currentUser.uid, data, getUserOrders.data]);
}
export default AddOrder;
请检查代码是否有效,有任何问题请评论以便我可以更正我的答案(我还没有测试过,但我相信它会有效)。
虽然不推荐,但是因为good practice推荐使用memos,你可以尝试在最后一种情况下(只是为了验证这是否真的是问题所在)使用如下:
[currentUser.uid, JSON.stringify(数据), JSON.stringify(getUserOrders.data)]
我在我的项目中使用 Stripe API,当付款成功后,我想将下的订单添加到我的 collection。当我 运行 以下代码时,它多次添加订单并且没有特定模式。它还将多个订单添加到 collection:
Success.js:
import React from "react";
import AddOrder from "../orders/AddOrder";
import { useNavigate, useParams } from "react-router-dom";
import { useMutation, useQuery } from "@apollo/client";
import queries from "../../queries";
import { Alert, Button } from "react-bootstrap";
function Success() {
const logo = require("../../assets/delivery-package.gif");
const navigate = useNavigate();
const { secret } = useParams();
const { loading, error, data } = useQuery(queries.GET_SESSION, { variables: { id: secret } });
const [deleteSession] = useMutation(queries.DELETE_SESSION);
if (loading) {
return <div>Loading...</div>;
} else if (error) {
navigate("/notfound");
} else if (data.session) {
deleteSession({
variables: {
id: secret,
},
});
return (
<div>
<AddOrder />
<Alert variant="success" style={{ fontSize: "25px" }}>
Order Placed Successfully
</Alert>
<img alt="order success" id="logo" src={logo} style={{ width: "70%", height: "70%", marginTop: "30px" }} />
<br />
<br />
<Button onClick={() => navigate("/")}>Home</Button>
</div>
);
} else {
navigate("/notfound");
}
}
export default Success;
添加订单:
import { useMutation, useQuery } from "@apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [addOrder, currentUser, data, editUser, getUserOrders, text]);
}
export default AddOrder;
只有一个return不知道是成功页面的问题还是Addorder函数的问题
在 AddOrder 组件中,您没有 returning 任何 JSX。您实际上是在组件主体中发送订单请求(在渲染期间,这非常糟糕,因为它是 side-effect)。
不要return addOrder、editUser 调用。根据您的业务逻辑将它们放在 useEffect 或事件处理程序中。
可能原因:
您的组件必须由某个父组件呈现,例如,通过调用 setState,并且当该组件是 re-rendered Success 子组件(反过来又不会 return JSX.Element) 也是 re-rendered
原因:
在你的AddOrder
组件中有一个 useEffect
用来防止 re-renders 当传递的参数没有改变时,其中有一个 "new Date().getTime()",我相信这是一个useEffect
总是 return 为真并请求 re-render 的主要原因。不仅如此,还有:new Date().toString 作为
useEffect
的参数将函数(addOrder、editOrder)传递给
useEffect
重新渲染时可能总是新函数的参数。currentUser
可能是每个渲染的新对象,幸运的是你只需要 userid,它又是一个原始类型,并且无论 re-rendering.
解决方案:
addOrder
或 editOrder
函数是否发生了变化,无需分析,因为它们只是函数,不要添加任何具体的 "value",例如,如果您的意图是重新渲染(为了能够添加)只有当项目发生变化时,你可以只留下 getUserOrders.data
和 data
(useQuery(queries.GET_USER_BY_ID)...)
新代码:
import { useMutation, useQuery } from "@apollo/client";
import { AuthContext } from "../../Firebase/Auth";
import queries from "../../queries";
import { useContext, useEffect } from "react";
import { reactLocalStorage } from "reactjs-localstorage";
let add = reactLocalStorage.getObject("addressDetails");
function AddOrder() {
const d = new Date();
let text = d.toString();
const [addOrder] = useMutation(queries.ADD_ORDER);
const [editUser] = useMutation(queries.EDIT_USER_CART);
const { currentUser } = useContext(AuthContext);
const { data } = useQuery(queries.GET_USER_BY_ID, {
fetchPolicy: "network-only",
variables: {
id: currentUser.uid,
},
});
const getUserOrders = useQuery(queries.GET_USER_ORDERS, {
fetchPolicy: "network-only",
variables: {
userId: currentUser.uid,
},
});
useEffect(() => {
if (data && getUserOrders.data && currentUser && data.getUser.cart.length > 0) {
let newCart = [];
let total = 0;
for (let i = 0; i < data.getUser.cart.length; i++) {
total += data.getUser.cart[i].price * data.getUser.cart[i].quantity;
newCart.push({
orderedQuantity: data.getUser.cart[i].quantity,
_id: data.getUser.cart[i]._id,
name: data.getUser.cart[i].name,
image: data.getUser.cart[i].image,
price: data.getUser.cart[i].price,
});
}
addOrder({
variables: {
userId: currentUser.uid,
userEmail: currentUser.email,
status: "ordered",
createdAt: text,
products: newCart,
total: total,
flag: getUserOrders.data.userOrders.length + 1,
zip: add.zip.val ? add.zip.val : add.zip,
state: add.state.val ? add.state.val : add.state,
city: add.city.val ? add.city.val : add.city,
apt: add.apt.val ? add.apt.val : add.apt,
addressStreet: add.addressStreet.val ? add.addressStreet.val : add.addressStreet,
},
});
editUser({
variables: {
id: currentUser.uid,
cart: [],
},
});
}
}, [currentUser.uid, data, getUserOrders.data]);
}
export default AddOrder;
请检查代码是否有效,有任何问题请评论以便我可以更正我的答案(我还没有测试过,但我相信它会有效)。
虽然不推荐,但是因为good practice推荐使用memos,你可以尝试在最后一种情况下(只是为了验证这是否真的是问题所在)使用如下:
[currentUser.uid, JSON.stringify(数据), JSON.stringify(getUserOrders.data)]