为什么我的 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 的主要原因。不仅如此,还有:

  1. new Date().toString 作为 useEffect 的参数

  2. 将函数(addOrder、editOrder)传递给 useEffect 重新渲染时可能总是新函数的参数。

  3. currentUser 可能是每个渲染的新对象,幸运的是你只需要 userid,它又是一个原始类型,并且无论 re-rendering.

解决方案:

addOrdereditOrder 函数是否发生了变化,无需分析,因为它们只是函数,不要添加任何具体的 "value",例如,如果您的意图是重新渲染(为了能够添加)只有当项目发生变化时,你可以只留下 getUserOrders.datadata (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)]