Реакция рендера до получения реквизита - PullRequest
0 голосов
/ 13 апреля 2020

Я использую MapBox для отображения карты в профиле пользователя. Компонент получает реквизиты местоположения, но mapbox пытается отобразить карту с местоположением до получения реквизитов, ломая страницу. Я использовал обходной путь, чтобы заставить его работать ... но не уверен, правильно ли я это делаю - есть ли у вас какие-либо рекомендации? Мой обходной путь состоял в том, чтобы установить состояние viewPort, используя хук useEffect ... это заставляет карту ждать секунду перед загрузкой, давая время для получения реквизита. У меня есть ощущение, что он все еще может сломаться, если реквизит не получен вовремя

Я также хотел бы использовать местоположение пользователя в качестве центральной точки карты, которая в настоящее время устанавливается в хуке useEffect, но я не могу получить реквизит до того, как он будет установлен. Спасибо!

    import React, { useState, useEffect } from 'react';
import Geocode from "react-geocode";
import ReactMapGL, { Marker } from 'react-map-gl';
import { Label, Input, Card, Badge, Button, InputGroup,
} from 'reactstrap';

// set response language. Defaults to english.
Geocode.setLanguage("en");

// set response region. Its optional.
// A Geocoding request with region=es (Spain) will return the Spanish city.
Geocode.setRegion("es");

// set Google Maps Geocoding API for purposes of quota management. Its optional but recommended.
Geocode.setApiKey("******************************");

export default function Location(props) {
    const [ city, setCity ] = useState('');
    const [ viewPort, setViewport ] = useState({})

    useEffect(() => {
        setTimeout(() => {
            setViewport({
                latitude: 37.0902,
                longitude: -95.7129,
                width: '100%',
                height: '200px',
                zoom: 3
            })
        },1000)

    }, [])

    const captureCity = (e) => {
        setCity(e.target.value);
    }

    const handleLocationInput = () => {
        // Get latidude & longitude from address.
        Geocode.fromAddress(city).then(
            response => {
            const { lat, lng } = response.results[0].geometry.location;
            putLocation(lat, lng);
            },
            error => {
            console.error(error);
            }
        );
    }

    const putLocation = (lat, lon) => {
        props.updateBandLocation([lat,lon]);
    }

    return (
        <div>
            <div className="w-100">
                <ReactMapGL 
                    className="mt-2 mb-4 rounded"
                    style={{zIndex: "2"}}
                    {...viewPort} 
                    mapboxApiAccessToken={"******************"}
                    onViewportChange={viewport => setViewport(viewport)}
                    mapStyle="mapbox://styles/nickisyourfan/ck8ylgfk90kcb1iqoemkes76u"
                    >
                            <Marker latitude={props.bandLocation[0]} longitude={props.bandLocation[1]}>

                                <img src="/MapMarker.svg" alt="Band Icon" style={{width: "35px", height: "35px"}}/>

                            </Marker>

                </ReactMapGL> 
            </div>

            <Card color="dark" className="mb-1">
                <InputGroup className="d-flex flex-column justify-content-center">
                    <Label className="d-flex flex-column text-light h3 mt-2 align-self-center">Location</Label>
                    <Input type="text" onChange={captureCity} className="d-flex align-self-center w-75 mb-2"/>
                    <Button onClick={handleLocationInput} className="w-75 align-self-center mb-2">Set Location</Button>
                </InputGroup>
            </Card>
        </div>
    )
}

1 Ответ

0 голосов
/ 13 апреля 2020

Я бы не использовал setTimeout в этом сценарии.

Я бы просто не рендерил карту, пока у меня не будут все ожидаемые данные для карты. Я бы добавил это выше к моему заявлению о возвращении.

if (!props.bandLocation.length || !props.bandLocation[0] || !props.bandLocation[1]) {
return <MyCustomLoadingComp />
}

И в вашем конкретном случае c я не уверен, какой именно объект карты или структуры данных необходим, но вы можете понять концепцию. Например, если props.bandLocation [0] является пустым объектом, он все равно будет помечен как true, поэтому вы должны изменить оператор if, чтобы он соответствовал полученным данным.

Обновление: вы хотите использовать что-то вроде вспомогательной функции 'usePrevious'. Подобно componentWillReceiveProps, вам нужно что-то обновлять в вашем пользовательском интерфейсе только тогда, когда вы получаете определенный элемент. Таким образом, в этом случае вы можете проверить, получили ли вы правильный lat / long, а затем установить его с помощью useState. Мне нужно было повторить этот метод жизненного цикла с крючками, и я адаптировался из этого https://usehooks.com/usePrevious/. Тем не менее, по-прежнему рекомендуется просто использовать реквизит напрямую. Вы можете передать имя вашего реквизита в useEffect, и когда оно изменится, комп будет визуализироваться.

То есть

const [loaded, setLoaded] = useState(lat);
  useEffect(() => {
setLoaded(false);
if (isAValidLocation(props.lat)) {
  setLoaded(true);

}
}, [props.lat, props.long]);

Однако, чтобы быть тупым, вы не должны просто делать это. до тех пор, пока в ней не появятся все соответствующие данные, вам не нужно использовать какие-либо ловушки жизненного цикла, если вы не манипулируете содержимым реквизита каким-либо образом.

...