【问题标题】:Issue with calling a component's function while rendering inside a multiple ternary expression - react在多三元表达式中渲染时调用组件函数的问题 - 反应
【发布时间】:2020-11-10 22:03:32
【问题描述】:

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

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

在 product.jsx 中,我想根据 categoryName 渲染 8 个组件之一,我通过调用我在组件主体 (getCategory) 中定义的函数来获得它。但是,出于某种原因,getCategory 函数从未被评估过,也不会从该函数的控制台中打印任何 console.log。

需要指出的是,如果我只是渲染 ,则显示数据,如果第一个的比较是“处理器”===“处理器”(始终为真),则再次显示数据.

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

您将在下面找到相关组件的代码,但可以在此处找到完整的 repo: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 返回的对象不是您期望的字符串

标签: reactjs jsx conditional-operator


【解决方案1】:

正如当前编写的那样,您的getCategory 函数“返回”undefined,因为它没有返回语句,并且没有隐式返回,因为您使用的是{}。它应该被调用,它只是不会从内部循环返回任何东西。

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

这样做的一种方法是改用useMemo 挂钩,并跳过getCategory 函数。无需使用forEach 函数,只需使用 for 循环即可让您从内部循环返回一些内容。

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 函数来简化,它接受一个谓词并返回匹配的第一个项目。我们正在寻找第一个具有商品 ID 的商品 ID 的类别,因此我们可以使用 some 函数,如果数组中的 any 商品与谓词匹配,则该函数返回 true

// 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>}
</>

附带说明,由于您使用的是钩子,因此您还可以使用 useDispatch 钩子获取操作调度程序,而不是 connect

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

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

【讨论】:

    猜你喜欢
    • 2019-02-21
    • 1970-01-01
    • 2022-06-12
    • 2020-12-22
    • 1970-01-01
    • 2023-01-13
    • 2018-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多