在收到道具之前反应渲染
React Renders Before Props Recieved
我正在使用 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>
)
}
我不会在这种情况下使用 setTimeout。
在获得地图的所有预期数据之前,我不会渲染地图。
我会将其添加到我的 return 声明之上。
if (!props.bandLocation.length || !props.bandLocation[0] || !props.bandLocation[1]) {
return <MyCustomLoadingComp />
}
在你的具体情况下,我不确定地图究竟需要哪个道具或数据结构,但你可以理解这个概念。例如,如果 props.bandLocation[0] 是一个空对象,它仍然会标记为真,因此您必须更改 if 语句以匹配您正在接收的数据。
更新:
您可能想要使用类似 'usePrevious' 辅助函数的东西。与 componentWillReceiveProps 类似,只有当您获得某个项目时,您才需要更新 UI 中的内容。所以在这种情况下,你可以检查你是否得到一个有效的 lat/long,然后用 useState 设置它。我需要用钩子复制这个生命周期方法,我改编自这个 https://usehooks.com/usePrevious/。不过还是建议,直接使用props就可以了。您可以将道具的名称传递给useEffect,当它更改时,comp 将重新渲染。
所以
const [loaded, setLoaded] = useState(lat);
useEffect(() => {
setLoaded(false);
if (isAValidLocation(props.lat)) {
setLoaded(true);
}
}, [props.lat, props.long]);
然而,直言不讳,你不应该渲染你的地图,直到它有所有相关数据,如果你不以任何方式操纵道具内容,你不应该需要使用任何生命周期挂钩。
我正在使用 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>
)
}
我不会在这种情况下使用 setTimeout。
在获得地图的所有预期数据之前,我不会渲染地图。 我会将其添加到我的 return 声明之上。
if (!props.bandLocation.length || !props.bandLocation[0] || !props.bandLocation[1]) {
return <MyCustomLoadingComp />
}
在你的具体情况下,我不确定地图究竟需要哪个道具或数据结构,但你可以理解这个概念。例如,如果 props.bandLocation[0] 是一个空对象,它仍然会标记为真,因此您必须更改 if 语句以匹配您正在接收的数据。
更新: 您可能想要使用类似 'usePrevious' 辅助函数的东西。与 componentWillReceiveProps 类似,只有当您获得某个项目时,您才需要更新 UI 中的内容。所以在这种情况下,你可以检查你是否得到一个有效的 lat/long,然后用 useState 设置它。我需要用钩子复制这个生命周期方法,我改编自这个 https://usehooks.com/usePrevious/。不过还是建议,直接使用props就可以了。您可以将道具的名称传递给useEffect,当它更改时,comp 将重新渲染。
所以
const [loaded, setLoaded] = useState(lat);
useEffect(() => {
setLoaded(false);
if (isAValidLocation(props.lat)) {
setLoaded(true);
}
}, [props.lat, props.long]);
然而,直言不讳,你不应该渲染你的地图,直到它有所有相关数据,如果你不以任何方式操纵道具内容,你不应该需要使用任何生命周期挂钩。