#reactjs #react-hooks
Вопрос:
Я пытаюсь получить доступ к данным из API commerceJS, извлеченным из другого модуля и переданным в качестве поддержки, код прерывается только тогда, когда я вызываю вложенный объект продукта с этой ошибкой Uncaught TypeError: Cannot read properties of undefined (reading 'source')
.
Я использовал тот же API на витрине магазина и {product.media.source}
отлично работает. Я пытаюсь создать модуль предварительного просмотра, и когда я вызываю вложенный объект, он ломается. Пожалуйста, помогите.
Вот мой код
const Preview = ({ products, onAddToCart }) => {
const [product, setProduct] = useState({});
const params = useParams();
const findProduct = (id) => {
return products.find((product) => {
setProduct(product);
return product.id === id;
});
};
const productId = params.id;
useEffect(() => {
findProduct(productId);
});
return (
<Card sx={{ maxWidth: 345 }} style={{ marginTop: '90px' }}>
<CardMedia image={product.media.source} title={product.name}></CardMedia>
<CardContent>
<Typography gutterBottom variant='h5' component='div'>
{product.name}
</Typography>
<Typography
variant='body2'
color='textSecondary'
dangerouslySetInnerHTML={{ __html: product.description }}
></Typography>
</CardContent>
<CardActions>
<IconButton component={Link} variant='outlined' to='/'>
Go Back
</IconButton>
<IconButton aria-label='Add to Cart' onClick={() => onAddToCart(product.id, 1)}>
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
Вот объект продукта
объект продукта
Комментарии:
1. Очевидно, ваш
product
объект не содержитmedia
поля. Можете ли вы предоставить содержимоеproduct
объекта?2. Готово, вы должны найти там изображение в формате png.
Ответ №1:
По-видимому, ваш product
объект по умолчанию не содержит media
поля. Вам нужно подождать, пока он будет доступен, прежде чем использовать его.
Попробуйте что-нибудь вроде этого:
const Preview = ({ products, onAddToCart }) => {
const [product, setProduct] = useState();
const params = useParams();
console.log(products[0]);
console.log(params.id);
const findProduct = useCallback((id) => {
return products.find((product) => {
if (product.id === id) { // <-- render optimization
setProduct(product);
return true;
}
return false;
});
}, [products]);
const productId = useMemo(() => params.id, [params.id]);
useEffect(() => {
findProduct(productId);
}, [productId, findProduct]);
const renderCard = useCallback(() => {
if (!product) return null; // <-- here you can implement your placeholder component
return (
<Card sx={{ maxWidth: 345 }} style={{ marginTop: '90px' }}>
<CardMedia image={product.media.source} title={product.name}/>
<CardContent>
<Typography gutterBottom variant='h5' component='div'>
{product.name}
</Typography>
<Typography
variant='body2'
color='textSecondary'
dangerouslySetInnerHTML={{ __html: product.description }}
></Typography>
</CardContent>
<CardActions>
<IconButton component={Link} variant='outlined' to='/'>
Go Back
</IconButton>
<IconButton aria-label='Add to Cart' onClick={() => onAddToCart(product.id, 1)}>
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
}, [product]);
return renderCard();
};
Или вот так:
const initialState = { // <-- you need to change this as you need
active: true,
assets: [/* ... */],
categories: [/* ... */],
checkout_url: {
checkout: 'placeholder://checkout.url',
display: 'placeholder://display.url',
/* ... */
},
collects: {/*...*/}
created: 0,
description: 'placeholder description',
extra_fields: [/* ... */],
hsa: {/* ... */},
id: 'placeholder_id',
invenotry: {/* ... */},
is: {/* ... */},
media: {
type: 'image',
source: 'placeholder://image.source',
/* ... */
},
meta: null,
/* ... */
};
const Preview = ({ products, onAddToCart }) => {
const [product, setProduct] = useState(initialState);
const params = useParams();
console.log(products[0]);
console.log(params.id);
const findProduct = useCallback((id) => {
return products.find((product) => {
if (product.id === id) { // <-- render optimization
setProduct(product);
return true;
}
return false;
});
}, [products]);
const productId = useMemo(() => params.id, [params.id]);
useEffect(() => {
findProduct(productId);
}, [productId, findProduct]);
return (
<Card sx={{ maxWidth: 345 }} style={{ marginTop: '90px' }}>
<CardMedia image={product.media.source} title={product.name}/>
<CardContent>
<Typography gutterBottom variant='h5' component='div'>
{product.name}
</Typography>
<Typography
variant='body2'
color='textSecondary'
dangerouslySetInnerHTML={{ __html: product.description }}
></Typography>
</CardContent>
<CardActions>
<IconButton component={Link} variant='outlined' to='/'>
Go Back
</IconButton>
<IconButton aria-label='Add to Cart' onClick={() => onAddToCart(product.id, 1)}>
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
};
Комментарии:
1. Это работает, это работает! Спасибо!! :D, Загрузка занимает некоторое время, можете ли вы сказать, почему?
2. @CaramelIfy время, необходимое для загрузки, зависит от внутренней логики используемых крючков, а также логики родительских компонентов. Скорее всего, вы где-то делаете запрос api, это требует времени
3. @CaramelIfy даже если бы время загрузки было чрезвычайно коротким, скорее всего, вы все равно получили бы ту же ошибку, потому что в своем коде вы изначально пытались получить доступ к полю
undefined
объекта4. Что ж, я думаю, в этом есть смысл @wowandy еще раз спасибо!
5. @CaramelIfy добро пожаловать 🙂
Ответ №2:
На основе вашего кода выше
console.log(products[0]);
console.log(params.id);
const findProduct = (id) => {
return products.find((product) => {
setProduct(product);
return product.id === id;
});
};
Похоже, что продукт представляет собой массив, а не объект. Я думаю, что это то, что вы, возможно, пропустили.
Следовательно, product.media.source
выдает ошибку, поскольку product.media не определен.
Пожалуйста, проверьте, какой продукт вы, возможно, захотите добавить.