如何将数据传输到另一个组件(useEffect问题)



在useEffect中,我从服务器检索数据并将其存储在"产品"中。数组:

const { url } = props;
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
setLoadingSpinner(true);
const response = await fetch(url);
const responseData = await response.json();
setLoadingSpinner(false);
const loadedProducts = [];
for (const key in responseData) {
loadedProducts.push({
id: key,
name: responseData[key].name,
description: responseData[key].description,
price: responseData[key].price,
image: responseData[key].image,
processor: responseData[key].processor,
});
}
setProducts(loadedProducts);
setIsDataLoaded(true);
};
fetchProducts();
}, [url, isDataLoaded]);

我将它传递给ProductDetail组件:

<ProductDetail products={products}></ProductDetail>

我总是在ProductDetail组件中得到一个空值:

function ProductDetail(props) {
const params = useParams();
const [product, setProduct] = useState(null);

useEffect(() => {
if (props.products && params.productId) {
const product = props.products.find(product => product.id === params.productId);
setProduct(product);
}
}, [props.products, params.productId]);
console.log(product)

我意识到,当useEffect第一次运行时,我的"产品"数组仍然是空的,它将它发送给组件,所以这不是很好。但我不知道如何解决这个问题。

这里有几个错误。我将从一个乍一看似乎无关紧要的问题开始,但它会引出一个更好的答案。

ProductDetail中,你引入了一个叫做product的新状态。然后,在useEffect中,从列表&props中的Product id,并将其设置回状态项。这将是不必要的状态复制(即使没有将prop逐字复制到state中,直接从props派生的值仍然可以计算),并且将增加应用程序中bug的表面积(这可能是最常见的初学者错误)。

你实际上不需要状态项,你只需要useMemo:

function ProductDetail(props) {
const params = useParams();

const product = useMemo(() => {
if (props.products && params.productId) {
return props.products.find(product => product.id === params.productId);
}
}, [props.products, params.productId]);
console.log(product)

为了解决产品加载时没有被发现的问题,你可以(a)有条件地渲染组件,这样当products还没有被获取时,这个代码甚至不会在第一时间运行。或者(b)更改ProductDetail,使其在找到产品之前有效地不做任何事情。

const [products, setProducts] = useState(null); // We change the default to null to be able to distinguish empty list case from loading case.
// ...
{products !== null && <ProductDetail products={products}></ProductDetail>}

解决方案B

function ProductDetail(props) {
const params = useParams();

const product = useMemo(() => {
if (props.products && params.productId) {
return props.products.find(product => product.id === params.productId);
}
}, [props.products, params.productId]);
if (!product) return null // Return early (do nothing) whilst product is not found
// ... your original render contents