Reputation: 79
Hi guys I got a problem showing data with different way of my product if u know bradtraversy new course about ecommerce, i tried to make modification on my code after Im done with the course
first GET api/products/:id
this api get product id on the mongoDb and its default from the course
second im going to call it with different way using slug i already create slug here at mongoDb but the problem is the message say
TypeError: Cannot read property 'length' of undefined
so i check it by myself my product not showing the data in the front end but showing the data in the backend
so ill bring u guys the code here :
Backend productController
const getProductBySlug = asyncHandler(async (req, res) => {
const product = await Product.find({ slug: req.params.slug })
console.log(product)
if (product) {
res.json(product)
} else {
res.status(404)
throw new Error('Product not found')
}
})
this is my product reducer
export const productDetailsReducer = (
state = { product: { reviews: [] } },
action
) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
return { loading: true, ...state }
case PRODUCT_DETAILS_SUCCESS:
return { loading: false, product: action.payload }
case PRODUCT_DETAILS_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
}
productAction
export const listProductDetails = (slug) => async (dispatch) => {
try {
dispatch({ type: PRODUCT_DETAILS_REQUEST })
const { data } = await axios.get(`/api/products/slug/${slug}`)
dispatch({
type: PRODUCT_DETAILS_SUCCESS,
payload: data,
})
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message
if (message === 'Not authorized, token failed') {
dispatch(logout())
}
dispatch({
type: PRODUCT_DETAILS_FAIL,
payload: message,
})
}
}
in the redux extentions chrome its success called but its show error after sucess
any clue about that ? this is the result in console using morgan
EDIT :
i decided to include my productScreen
import {
listProductDetails,
createProductReview,
} from '../actions/productActions'
import { PRODUCT_CREATE_REVIEW_RESET } from '../constants/productsConstants'
const ProductScreen = ({ history, match }) => {
const [qty, setQty] = useState(1)
const [rating, setRating] = useState(0)
const [comment, setComment] = useState('')
const dispatch = useDispatch()
const userLogin = useSelector((state) => state.userLogin)
const { userInfo } = userLogin
const productDetails = useSelector((state) => state.productDetails)
const { loading, error, product } = productDetails
const productCreateReview = useSelector((state) => state.productCreateReview)
const {
success: successProductReview,
// loading: loadingProductReview,
error: errorProductReview,
} = productCreateReview
useEffect(() => {
if (successProductReview) {
alert('Review Submitted!')
setRating(0)
setComment('')
dispatch({ type: PRODUCT_CREATE_REVIEW_RESET })
}
dispatch(listProductDetails(match.params.slug))
}, [dispatch, match, successProductReview])
const addToCardHandler = () => {
history.push(`/cart/${match.params.slug}?qty=${qty}`)
}
const submitHandler = (e) => {
e.preventDefault()
dispatch(
createProductReview(match.params.slug, {
rating,
comment,
})
)
}
return (
<>
<Link className='btn btn-light my-3' to='/'>
Go Back
</Link>
{loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<>
<Meta title={product.name} />
<Row>
<Col md={6}>
<Image src={product.image} alt={product.name} fluid />
</Col>
<Col md={3}>
<ListGroup variant='flush'>
<ListGroup.Item>
<h3>{product.name}</h3>
</ListGroup.Item>
<ListGroup.Item>
<Rating
value={product.rating}
text={`${product.numReviews} Reviews`}
/>
</ListGroup.Item>
<ListGroup.Item>Price : ${product.price}</ListGroup.Item>
<ListGroup.Item>
Description : ${product.description}
</ListGroup.Item>
</ListGroup>
</Col>
<Col md={3}>
<Card>
<ListGroup variant='flush'>
<ListGroup.Item>
<Row>
<Col> Price : </Col>
<Col>
<strong>${product.price}</strong>
</Col>
</Row>
</ListGroup.Item>
<ListGroup.Item>
<Row>
<Col> Status : </Col>
<Col>
{product.countInStock > 0 ? 'In Stock' : 'Out Of Stock'}
</Col>
</Row>
</ListGroup.Item>
{product.countInStock > 0 && (
<ListGroup.Item>
<Row>
<Col>Qty</Col>
<Col>
<Form.Control
as='select'
value={qty}
onChange={(e) => setQty(e.target.value)}
>
{[...Array(product.countInStock).keys()].map(
(x) => (
<option key={x + 1} value={x + 1}>
{x + 1}
</option>
)
)}
</Form.Control>
</Col>
</Row>
</ListGroup.Item>
)}
<ListGroup.Item>
<Button
onClick={addToCardHandler}
className='btn-block'
type='button'
disabled={product.countInStock === 0}
>
Add To Card
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
<Row>
<Col md={6}>
<h2>Reviews</h2>
{product.reviews.length === 0 ? (
<Message>No Reviews</Message>
) : (
<ListGroup variant='flush'>
{product.reviews.map((review) => (
<ListGroup.Item key={review._id}>
<strong>{review.name}</strong>
<Rating value={review.rating} />
<p>{review.createdAt.substring(0, 10)}</p>
<p>{review.comment}</p>
</ListGroup.Item>
))}
<ListGroup.Item>
<h4>Write a Customer Review</h4>
{errorProductReview && (
<Message variant='danger'>{errorProductReview}</Message>
)}
{userInfo ? (
<Form onSubmit={submitHandler}>
<Form.Group controlId='rating'>
<Form.Label>Rating</Form.Label>
<Form.Control
as='select'
value={rating}
onChange={(e) => setRating(e.target.value)}
>
<option value=''>Select...</option>
<option value='1'>1 - Poor</option>
<option value='2'>2 - Fair</option>
<option value='3'>3 - Good</option>
<option value='4'>4 - Very Good</option>
<option value='5'>5 - Excellent</option>
</Form.Control>
</Form.Group>
<Form.Group controlId='comment'>
<Form.Label>Comment</Form.Label>
<Form.Control
as='textarea'
row='3'
value={comment}
onChange={(e) => setComment(e.target.value)}
></Form.Control>
</Form.Group>
<Button type='submit' variant='primary'>
Submit
</Button>
</Form>
) : (
<Message>
Please <Link to='/login'>Sign in</Link> to write a
review
</Message>
)}
</ListGroup.Item>
</ListGroup>
)}
</Col>
</Row>
</>
)}
</>
)
}
export default ProductScreen
Upvotes: 0
Views: 248
Reputation: 347
are you sure action.payload are product object or array(1) [0 => productObject]
Upvotes: 2