Reputation: 77
I'm building a ReactJS Mapbox project with MapBox GL JS
.
My code demo below:
const Map = () => {
var map;
const [testState, setTestState] = useState(1);
const testApp = () => {
console.log(typeof map);
setTestState(2); // set state
map.easeTo({
....//some code
})
}
useEffect(() =>{
map = new mapboxgl.Map({
container: "mapContainer",
style: "mapbox://styles/mapbox/streets-v9",
center: [0, 0],
zoom: 12,
});
},[]) //I tried remove [] here
}
.....// render
<button onClick={() => testApp()/>
export default Map;
When I run onClick={() => testApp()}
on the first time everything's be OK. But in second time. I got:
Uncaught TypeError: Cannot read property 'easeTo' of undefined
and the undefined
in console.log(typeof map)
.
I tried remove []
on useEffect
, the map.easeTo
worked but the map will reload when I run testApp()
(Why????).
I want each time when I click button
the Map do not reload and function testApp()
will run with my code above.
How can I do that ?
Upvotes: 0
Views: 618
Reputation: 600
So let's go through this step by step.
I tried remove [] on useEffect, the map.easeTo worked but the map will reload when I run testApp() (Why????).
By removing []
from the useEffect
hook, the code inside the hook will run on every component render. That's why the map reloads, because every render you create new instance of the mapboxgl.Map
class.
I suggest you take a look at the useEffect docs and compare the difference without the dependency array, with an empty dep array ([]
) or with actual dependencies.
When I run onClick={() => testApp()} on the first time everything's be OK. But in second time. I got: Uncaught TypeError: Cannot read property 'easeTo' of undefined
Whenever you see this type of error "Cannot read property X of undefined", it means that you called a method on an object that is undefined
. In this case, during the second render, the var map;
is undefined
.
In React's functional component, you have to use either useState
or useRef
to persists the state between renders.
The component is function after all, so every time it gets called, new context is created and the variables are undefined.
Lastly, you can instantiate the mapboxgl.Map
outside of the component, in the file context, if you expect the use the same map during the whole runtime of your app. Like so:
const MAP_INSTANCE = new mapboxgl.Map({
container: "mapContainer",
style: "mapbox://styles/mapbox/streets-v9",
center: [0, 0],
zoom: 12,
});
const Map = () => {
const [testState, setTestState] = useState(1);
const testApp = () => {
console.log(typeof map);
setTestState(2); // set state
MAP_INSTANCE.easeTo({
// Some code
})
}
return <button onClick={() => testApp()} />
};
export default Map;
Upvotes: 1