предотвращение использования состояния для отображения начального значения

#javascript #reactjs

Вопрос:

Если вы посмотрите на мой JSX, я пытаюсь создать размеры из product объекта, проблема в том, что, когда я зацикливаюсь product.size.map() , он выдает ошибку, в которой говорится product , что она не определена.

Как это решить?

обновление Мне удалось выполнить некоторые обходные пути, но я уверен, что это не считается лучшей практикой.

 import React , {useState , useEffect} from 'react'
import { Row, Col, Image, ListGroup, Card, Button } from 'react-bootstrap'
import {Link} from 'react-router-dom'
import Rating from '../components/Rating'
import axios from 'axios'


const ProductScreen = ({match}) => {
    const [product , setProduct] = useState({})   
    useEffect(()=>{
        let componentMounted = true
        const fetchProducts =  async ()=>{
            const {data} = await axios.get(`http://172.30.246.130:5000/api/products/${match.params.id}`)
            if(componentMounted){
                setProduct(data) 
            }
        }
        fetchProducts()
        return () => {
            componentMounted = false
        }
    },[match])
    if(Object.keys(product).length===0) return null // this is the workaround I have done what do you think?
    else{
    return (
        <>
       
            <Link className='btn btn-light my-3' to='/'>
            Go Back
            </Link>
            <Row>
                <Col md={6}>
                <Image src={product.image} alt={product.name} />
                </Col>
                <Col md={3}>
                    <ListGroup variant='flush'>
                        <ListGroup.Item>
                <h3>{product.name}</h3>
                        </ListGroup.Item>

                <ListGroup.Item>
                    <Rating rating={product.rating} reviews={product.numReviews}/>
                </ListGroup.Item>
                <ListGroup.Item>
                <strong> Price: ${product.price}</strong>
                </ListGroup.Item>
                <ListGroup.Item>
                    <strong>Description :</strong> {product.description}
                </ListGroup.Item>
                    </ListGroup>
                </Col>
                <Col md={3}>
                    <Card>
                        <ListGroup>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Price :
                                    </Col>
                                    <Col>
                                    <strong>${product.price}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Status :
                                    </Col>
                                    <Col>
                                    <strong>{product.countInStock > 0 ? 'In Stock' : "Out Of Stock"}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Sizes :
                                    </Col>
                                    <Col>
                                  {product.size.map(s=><Button key={s} className='p-2 m-1'>{s}</Button>)}
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item className='d-grid gap-2'>
                                <Button type='button' disabled={product.countInStock === 0}>
                                    Add To Cart
                                </Button>
                            </ListGroup.Item>
                        </ListGroup>
                    </Card>
                
                </Col>
            </Row>
        </>
    )
}}

export default ProductScreen 
 import React , {useState , useEffect} from 'react'
import { Row, Col, Image, ListGroup, Card, Button } from 'react-bootstrap'
import {Link} from 'react-router-dom'
import Rating from '../components/Rating'
import axios from 'axios'


const ProductScreen = ({match}) => {
    const [product , setProduct] = useState({})   
    useEffect(()=>{
        let componentMounted = true
        const fetchProducts =  async ()=>{
            const {data} = await axios.get(`http://172.30.246.130:5000/api/products/${match.params.id}`)
            if(componentMounted){
                setProduct(data) 
            }
        }
        fetchProducts()
        return () => {
            componentMounted = false
        }
    },[match,product ])
    
    return (
        <>
            <Link className='btn btn-light my-3' to='/'>
            Go Back
            </Link>
            <Row>
                <Col md={6}>
                <Image src={product.image} alt={product.name} />
                </Col>
                <Col md={3}>
                    <ListGroup variant='flush'>
                        <ListGroup.Item>
                <h3>{product.name}</h3>
                        </ListGroup.Item>

                <ListGroup.Item>
                    <Rating rating={product.rating} reviews={product.numReviews}/>
                </ListGroup.Item>
                <ListGroup.Item>
                <strong> Price: ${product.price}</strong>
                </ListGroup.Item>
                <ListGroup.Item>
                    <strong>Description :</strong> {product.description}
                </ListGroup.Item>
                    </ListGroup>
                </Col>
                <Col md={3}>
                    <Card>
                        <ListGroup>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Price :
                                    </Col>
                                    <Col>
                                    <strong>${product.price}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Status :
                                    </Col>
                                    <Col>
                                    <strong>{product.countInStock > 0 ? 'In Stock' : "Out Of Stock"}</strong>
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item>
                                <Row>
                                    <Col>
                                    Sizes :
                                    </Col>
                                    <Col>
                                   {console.log(product)}
                                    </Col>
                                </Row>
                            </ListGroup.Item>
                            <ListGroup.Item className='d-grid gap-2'>
                                <Button type='button' disabled={product.countInStock === 0}>
                                    Add To Cart
                                </Button>
                            </ListGroup.Item>
                        </ListGroup>
                    </Card>
                
                </Col>
            </Row>
        </>
    )
}

export default ProductScreen 

Комментарии:

1.«он выдал ошибку, в которой говорится, что продукт не определен». Держу пари, что он сказал product.size undefined (нет product ). «когда я зацикливаюсь на product.size.map() …» product.size.map нигде не появляется в предоставленном вами коде.

Ответ №1:

Вы не можете предотвратить отображение начального состояния.

(Я добавил обновление внизу, связанное с обновленным вопросом.)

Если этот компонент не может быть с пользой отрисован без product , то product он должен быть опорой, которую он получает, а не членом государства, которого он выбирает. Выборка должна быть в родительском компоненте.

Но если вы хотите, чтобы выборка осталась в компоненте, вам нужно установить значение маркера (например null ) и разветвить рендеринг на основе этого значения маркера, например:

 if (!product) {
    return /* ...render a "loading" message... */;
}
// ..render `product` here...
 

Повторите свой отредактированный вопрос: Похоже, у вас возникли проблемы product.size.map(/*...*/) . Если мы посмотрим на ваше начальное состояние, у вас есть:

 const [product, setProduct] = useState({});
 

Так как у него нет size свойства, product.size есть undefined и product.size.map произойдет сбой с ошибкой о product.size бытии undefined .

Вы не показали код , который пытается это сделать product.size.map , но у вас есть различные варианты.

Тебе бы не помешал охранник:

 {product.size amp;amp; product.size.map(/*...*/)}
 

Вы можете включить пустой массив в исходное состояние:

 const [product, setProduct] = useState({size: []});
 

Комментарии:

1. Спасибо, было бы мило с вашей стороны, если бы вы могли проверить мой отредактированный вопрос, поскольку он кажется реальной проблемой, с которой я сталкиваюсь.

2. Большое вам спасибо за ваше решение, теперь моя проблема решена, и это имеет смысл.

3. Рад, что это помогло! 🙂 Счастливого кодирования!

Ответ №2:

Удалите product из зависимостей useEffect as, которые вызовут обратный вызов и вызовут бесконечный цикл.

 useEffect(() => {
  ...      
},[match])
 

Чтобы ничего не отображать до тех пор, пока product оно не будет извлечено, используйте ложное значение, например undefined/null , в качестве начального состояния:

 const [product , setProduct] = useState();
 

и проверьте product , прежде чем повторно использовать JSX:

 if(!product) return null;

return (
 <>
  <Link className='btn btn-light my-3' to='/'>
 ...
)
 

Комментарии:

1. Хороший момент в отношении зависимости, я бы даже использовал match.params.id , у match могут быть другие параметры, от которых зависят другие компоненты, но у этого нет. Возможно, это сэкономит некоторые рендеры. Короткое замыкание с возвращением нуля-это то, что я тоже делаю, если выборка, скорее всего, будет короткой.

2. @Ramesh Reddy спасибо за ваш ответ, если вы посмотрите на мой jsx, я использую product объект, и когда я регистрирую его в консоли, кажется, что у него два значения: первое {} и второе-объект, который я хочу. проблема в том, что когда я визуализирую изображение с person объекта, он сначала говорит «неопределенный», что для меня очень странно.

3. Я отредактировал свой вопрос в соответствии с проблемой, с которой я в настоящее время сталкиваюсь

4. @TankOne Вы все еще можете следить за моим ответом, чтобы исправить ошибку в обновленном вопросе