在收到道具之前反应渲染

时间:2020-04-13 16:37:11

标签: reactjs mapbox react-props use-effect

我正在使用MapBox在用户个人资料上显示地图。该组件会接收位置的道具,但是mapbox会在接收到道具之前尝试使用该位置渲染地图,从而破坏页面。我使用了一种解决方法来使其正常工作...但是不确定我是否做对了-您有什么建议吗?我的解决方法是使用useEffect Hook设置viewPort状态...这迫使地图在加载之前等待一秒钟,以便有时间接收道具。我有一种感觉,如果没有及时收到道具,它仍然会损坏。

我还想将用户的位置用作地图的中心点,该点当前在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 :(得分:0)

在这种情况下,我不会使用setTimeout。

在获得所有期望的地图数据之前,我不会渲染地图。 我将其添加到我的return语句上方。

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

在您的特定情况下,我不确定地图到底需要哪个道具或数据结构,但是您可以理解这个概念。例如,如果props.bandLocation [0]是一个空对象,它将仍然标记为true,因此您必须更改if语句以匹配要接收的数据。

更新: 您可能想使用类似“ usePrevious”帮助函数的方法。与componentWillReceiveProps相似,仅在获得特定项目时才需要在UI中进行更新。因此,在这种情况下,您可以检查是否有有效的经/纬度,然后使用useState进行设置。我需要使用钩子来复制此生命周期方法,并且从https://usehooks.com/usePrevious/开始改编。但是,仍然建议直接使用道具。您可以将道具名称传递给useEffect,当其更改时,组合将重新呈现。

所以

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

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

不过,直率地说,您应该在地图具有所有相关数据之前不渲染地图,如果您不以任何方式操纵道具的内容,则无需使用任何生命周期挂钩。