在多个三元表达式中渲染时调用组件函数的问题 - React

Issue with calling a component's function while rendering inside a multiple ternary expression - react

因此,我有 8 个不同的组件,其中包含有关 8 类产品(pc 组件)的数据,这些组件在描述中显然具有不同的值(核心数等)。

这 8 个组件仅包含特定数据(处理器包含核心数,但不包含价格 - 每个组件都有价格,与类别无关)

在 product.jsx 中,我想根据 categoryName 渲染 8 个组件之一,我通过调用在组件主体 (getCategory) 中定义的函数获得该名称。然而,出于某种原因,getCategory 函数从未被评估过,没有 console.log 从该函数打印在控制台中。

需要指出的是,如果我只渲染 ,就会显示数据,如果第一个的比较是“processor”==="processor"(始终为 true),那么数据是再次显示。

我认为我的错误在于反应逻辑。不知何故,我遗漏了一些关于反应如何呈现东西的东西。你能发现我使用 React 时的错误吗?谢谢!

您将在下面找到相关组件的代码,但可以在此处找到完整的代码库:https://github.com/vradu007/radu-shop

import React, { useState, useEffect } from "react";
import Layout from "../../components/Layout/Layout";
import products from "../../utils/products.json";
import "./Product.css";
import { connect } from "react-redux";
import { addToCart } from "../../redux/cart/cartAction";
import { addToFavourites } from "../../redux/favourites/favouritesAction";
import { Processor, VideoCard, PowerSupply, Storage, Ram, Motherboard } from "../../utils/ProductItemsData";


const Product = (props) => {
    const [product, setProduct] = useState({});
    const { match } = props;
    const productId = match.params.productId;

    const categoryValues = Object.values(products);
    
    //unreachable
    const getCategory = () => {
        categoryValues.forEach((category) => {
            category.items.forEach((item)=> {
                if(item.id === productId){
                    return category;
                }
            })
        })
    }

    useEffect(() => {
        const productItems = categoryValues.reduce((acc, category) => {
            return [...acc, ...category.items];
        }, []);
        const currentProduct = productItems.find((product) => {
            return Number(productId) === product.id;
        });
        setProduct(currentProduct);
    }, [props]);
    

    return (
        <Layout>
            <div className="product-page container-fluid container-min-max-width">
                <h1 className="my-5 h2">{product.name}</h1>
                <div className="product-info d-flex">
                    <div className="image-wrapper d-flex mr-5">
                        <img src={product.image} alt="Product presentation" />
                    </div>
                    <div className="product-details">
                        <p className="h3 text-danger">
                            {product.price} {product.currency}
                        </p>
                        <div>
                            <button
                                className="btn btn-dark mb-4 font-weight-bold"
                                onClick={() => {
                                    props.addToCart({
                                        product: {
                                            id: product.id,
                                            name: product.name,
                                            price: product.price,
                                            currency: product.currency,
                                            image: product.image,
                                        },
                                    });
                                }}
                            >
                                Add to cart
                            </button>
                        </div>
                        <div>
                            <button
                                className="btn btn-dark mb-4 font-weight-bold"
                                onClick={() => {
                                    props.addToFavourites({
                                        product: {
                                            id: product.id,
                                            name: product.name,
                                            price: product.price,
                                            currency: product.currency,
                                            image: product.image,
                                        },
                                    });
                                }}
                            >
                                Add to favourites
                            </button>
                        </div>
                        {
                            getCategory()==="processor"?
                                <Processor
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : getCategory()==="video-card"?
                                <VideoCard
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : getCategory()==="motherboard"?
                                <Motherboard
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : getCategory()==="ram"?
                                <Ram
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : getCategory()==="storage"?
                                <Storage
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : getCategory()==="power-supply"?
                                <PowerSupply
                                    baseFrequency={product.baseFrequency}
                                    boostFrequency={product.boostFrequency}
                                    cores={product.cores}
                                    threads={product.threads}
                                    description={product.description}
                                />
                            : null
                        }
                        
                    </div>
                </div>
            </div>
        </Layout>
    );
};

function mapDispatchToProps(dispatch) {
    return {
        addToCart: (payload) => dispatch(addToCart(payload)),
        addToFavourites: (payload) => dispatch(addToFavourites(payload)),
    };
}

export default connect(null, mapDispatchToProps)(Product);

按照目前的写法,你的 getCategory 函数“returns” undefined,因为它没有 return 语句,也没有隐式 return 因为您正在使用 {}。它应该被调用,它只是不return来自内部循环的任何东西。

还要注意 forEach 没有 return 值,因此您需要重新考虑如何找到内部类别(而不是使用 forEach)。如果合适的话,我还建议您将 getCategory 的结果设置为一个变量并切换它,以免在渲染中多次计算类别值。

一种方法是改用 useMemo 挂钩,并跳过 getCategory 函数。无需使用 forEach 函数,只需使用 for 循环即可让您 return 来自内部循环的内容。

const category = useMemo(() => {
  for (const category of categoryValues) {
    for (const item of category.items) {
      if (item.id === productId) {
        return category;
      }
    }
  }
  return undefined;
}, [categoryValues, productId])

这可以通过 find function on an Array, which takes a predicate and returns the first item that match. We're looking for the first category that has an item id with the product ID, so we can use the some 函数来简化,其中 returns true 如果数组中的 any 项匹配谓词:

// useMemo takes a function that is called when its dependencies change.
// we use a lambda - () => expression - so the return value is implicit
const category = useMemo(() => 
  categoryValues.find(category => category.items.some(item => item.id === productId))
  // You can use destructuring instead if you find that more readable
  // categoryValues.find(category => category.items.some(({ id }) => id === productId))
), [categoryValues, productId])

现在有了 useMemo 钩子,只有当 categoryValuesproductId 发生变化时才会重新计算值。

现在您应该可以在渲染中使用 category 变量而不是 getCategory() 来获得您想要的效果。请记住,您应该比较类型或名称而不是 category 项目,因为那是一个对象。

<>
  {category.name === "processor" ? <Processor />
   : category.name === "video-card" ? <VideoCard />
   // ...
   : <span>Could not find a suitable category</span>}
</>

作为旁注,因为您使用的是挂钩,而不是 connect,您还可以使用 useDispatch 挂钩获取操作调度程序。

const dispatch = useDispatch();
dispatch(addToCard(payload));

虽然这超出了问题的范围,但我个人更喜欢这种方法,以便尽可能避免 props