React ref.current 在 componentDidUpdate 中仍然是 null
React ref.current is still null in componentDidUpdate
我正在尝试使用 react-map-gl 放大一组坐标。为此,库鼓励我们使用 React 的 Refs。在 render
中实例化组件时,我传递了一个应该包含底层 MapBox 地图对象的引用。我设法使这种行为起作用。唯一的问题:它非常不一致。
我正在 componentDidUpdate
中调用一个名为 setCamera
的方法。但它只适用于初始负载。如果我重新加载页面,我会收到来自 Gatsby 的错误。如果我关闭错误,它将再次工作。直到下一次重新加载。
错误是this.mapRef.current
是null
。在尝试访问它之前,我试图设置一个条件来验证该值不是 null
,但这会导致动画永远无法工作。无论我重新加载多少次,它都不会执行。而如果没有条件,它至少可以在崩溃前工作一半的时间。所以这本身就是一个谜,如果有人知道为什么会发生这种行为,我洗耳恭听。
不过,我并没有气馁,而是尝试将对 setCamera
的调用放在 setTimeout
中,是的,它成功了!即使设置像 1 这样的非常低的超时,代码也能在 95% 的时间内正常工作。但我对此并不满意,因为我明白放置那种计时器不是我应该做的,而且让事情变得更糟的是,它并不能始终如一地解决问题。
我对这个问题的理解是,出于某种原因,componentDidUpdate
中仍未设置 MapRef。它有时会被设置。我不知道 React 是否支持线程,或者某种异步巫术是否在幕后欺骗我,但我想知道的是 我什么时候能保证我的 ref 被正确设置? 我应该在哪里或如何编写这段代码?
提前感谢任何可以帮助我的人。
这是我的示例代码:
import React, {Component} from 'react';
import MapGL, {NavigationControl, Source, Layer} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import gpxParser from 'gpxparser';
import bbox from '@turf/bbox';
const MAPBOX_TOKEN = 'exampleToken'
class HikeMapbox extends Component {
constructor(props) {
super(props)
this.state = {
gpxData: '',
};
}
// Read et get the GPX file, return it as a bunch of text
componentDidMount() {
const gpxPath = `https:${this.props.gpxPath}`;
// Simple GET request using fetch
fetch(gpxPath)
.then(response => response.text())
.then(text => this.setState({ gpxData: text }));
}
componentDidUpdate() {
// Set the camera on didUpdate
setTimeout(() => {
const geoJsonData = this.getGeoJson();
this.setCamera(geoJsonData);
}, 10);
}
// Get Max and Min Lat and Lon to Zoom on the GPX trace
setCamera(geoJsonData) {
if (geoJsonData) {
const [minLng, minLat, maxLng, maxLat] = bbox(geoJsonData);
this.mapRef.current.fitBounds(
[
[minLng, minLat],
[maxLng, maxLat]
],
{padding: 40}
);
}
}
// Take the text and parse it as geoJSON
getGeoJson() {
const { gpxData } = this.state;
let gpx = new gpxParser();
try {
gpx.parse(gpxData);
} catch (err) {
console.log(err);
}
const geoJson = gpx.toGeoJSON();
return geoJson
}
// Return the GPX trace in Mapbox
showGpxFile() {
const GeoJsonData = this.getGeoJson();
// Choose the style of the GPX trace
const layerStyle = {
id:'contours',
type:'line',
source:'contours',
paint: {
'line-color': 'blue',
'line-width': 3
}
};
return (
<>
{
// Return the React Mapbox GL code to show the GPX file on the map
GeoJsonData && (
<Source type="geojson" data={GeoJsonData}>
<Layer {...layerStyle} />
</Source>
)
}
</>
)
}
render() {
this.mapRef = React.createRef();
return (
<>
<MapGL
ref={this.mapRef}
initialViewState={{
latitude: this.props.latitude,
longitude: this.props.longitude,
zoom: 8,
}}
style={{width: "100%", height: "100%"}}
mapStyle="mapbox://styles/mapbox/outdoors-v11"
mapboxAccessToken={MAPBOX_TOKEN}
>
<NavigationControl />
{this.showGpxFile()}
</MapGL>
</>
)
}
}
export default HikeMapbox;
顺便说一下,我 运行 在我的计算机上使用 gatsby develop
这段代码。我不知道这是否相关,但我认为可能相关。
我找到了解决办法!
我的问题是 setCamera
取决于两个条件,并且这两个条件可能以任何顺序发生。
- 获取成功,我们有数据要显示。
- 地图已加载,我们有它的参考。
我没有在构造函数或 render
中初始化 mapRef
,而是创建了一个函数…
onMapRefChange = node => {
this.setState({mapRef: node});
// I'm calling my method now
this.setCamera()
};
... 我在 ref 参数中传递
<MapGL
ref={this.onMapRefChange}
...
最终 onMapRefChange
将收到一个实际的地图对象,然后 setCamera
中的代码将能够访问它。
我正在尝试使用 react-map-gl 放大一组坐标。为此,库鼓励我们使用 React 的 Refs。在 render
中实例化组件时,我传递了一个应该包含底层 MapBox 地图对象的引用。我设法使这种行为起作用。唯一的问题:它非常不一致。
我正在 componentDidUpdate
中调用一个名为 setCamera
的方法。但它只适用于初始负载。如果我重新加载页面,我会收到来自 Gatsby 的错误。如果我关闭错误,它将再次工作。直到下一次重新加载。
错误是this.mapRef.current
是null
。在尝试访问它之前,我试图设置一个条件来验证该值不是 null
,但这会导致动画永远无法工作。无论我重新加载多少次,它都不会执行。而如果没有条件,它至少可以在崩溃前工作一半的时间。所以这本身就是一个谜,如果有人知道为什么会发生这种行为,我洗耳恭听。
不过,我并没有气馁,而是尝试将对 setCamera
的调用放在 setTimeout
中,是的,它成功了!即使设置像 1 这样的非常低的超时,代码也能在 95% 的时间内正常工作。但我对此并不满意,因为我明白放置那种计时器不是我应该做的,而且让事情变得更糟的是,它并不能始终如一地解决问题。
我对这个问题的理解是,出于某种原因,componentDidUpdate
中仍未设置 MapRef。它有时会被设置。我不知道 React 是否支持线程,或者某种异步巫术是否在幕后欺骗我,但我想知道的是 我什么时候能保证我的 ref 被正确设置? 我应该在哪里或如何编写这段代码?
提前感谢任何可以帮助我的人。
这是我的示例代码:
import React, {Component} from 'react';
import MapGL, {NavigationControl, Source, Layer} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import gpxParser from 'gpxparser';
import bbox from '@turf/bbox';
const MAPBOX_TOKEN = 'exampleToken'
class HikeMapbox extends Component {
constructor(props) {
super(props)
this.state = {
gpxData: '',
};
}
// Read et get the GPX file, return it as a bunch of text
componentDidMount() {
const gpxPath = `https:${this.props.gpxPath}`;
// Simple GET request using fetch
fetch(gpxPath)
.then(response => response.text())
.then(text => this.setState({ gpxData: text }));
}
componentDidUpdate() {
// Set the camera on didUpdate
setTimeout(() => {
const geoJsonData = this.getGeoJson();
this.setCamera(geoJsonData);
}, 10);
}
// Get Max and Min Lat and Lon to Zoom on the GPX trace
setCamera(geoJsonData) {
if (geoJsonData) {
const [minLng, minLat, maxLng, maxLat] = bbox(geoJsonData);
this.mapRef.current.fitBounds(
[
[minLng, minLat],
[maxLng, maxLat]
],
{padding: 40}
);
}
}
// Take the text and parse it as geoJSON
getGeoJson() {
const { gpxData } = this.state;
let gpx = new gpxParser();
try {
gpx.parse(gpxData);
} catch (err) {
console.log(err);
}
const geoJson = gpx.toGeoJSON();
return geoJson
}
// Return the GPX trace in Mapbox
showGpxFile() {
const GeoJsonData = this.getGeoJson();
// Choose the style of the GPX trace
const layerStyle = {
id:'contours',
type:'line',
source:'contours',
paint: {
'line-color': 'blue',
'line-width': 3
}
};
return (
<>
{
// Return the React Mapbox GL code to show the GPX file on the map
GeoJsonData && (
<Source type="geojson" data={GeoJsonData}>
<Layer {...layerStyle} />
</Source>
)
}
</>
)
}
render() {
this.mapRef = React.createRef();
return (
<>
<MapGL
ref={this.mapRef}
initialViewState={{
latitude: this.props.latitude,
longitude: this.props.longitude,
zoom: 8,
}}
style={{width: "100%", height: "100%"}}
mapStyle="mapbox://styles/mapbox/outdoors-v11"
mapboxAccessToken={MAPBOX_TOKEN}
>
<NavigationControl />
{this.showGpxFile()}
</MapGL>
</>
)
}
}
export default HikeMapbox;
顺便说一下,我 运行 在我的计算机上使用 gatsby develop
这段代码。我不知道这是否相关,但我认为可能相关。
我找到了解决办法!
我的问题是 setCamera
取决于两个条件,并且这两个条件可能以任何顺序发生。
- 获取成功,我们有数据要显示。
- 地图已加载,我们有它的参考。
我没有在构造函数或 render
中初始化 mapRef
,而是创建了一个函数…
onMapRefChange = node => {
this.setState({mapRef: node});
// I'm calling my method now
this.setCamera()
};
... 我在 ref 参数中传递
<MapGL
ref={this.onMapRefChange}
...
最终 onMapRefChange
将收到一个实际的地图对象,然后 setCamera
中的代码将能够访问它。