i 'm getting some Kml's from server (by axios) and loading them on map:
// functional Component KmlManager ->
const map = useMap()
const addTrackAndBoundsFromKml = kmltext => {
const parser = new DOMParser();
var kml = parser.parseFromString(kmltext,"text/xml");
const track = new L.KML(kml);
map.addLayer(track);
};
React.useEffect(() => {
axios.get('url')
.then( res => {
const allLayouts = res.data
allLayouts.forEach(element => {addTrackAndBoundsFromKml(element.kml)})
})
})
code above is using This approach
it is working fine and loading Kml's. but now I want to add click listener and popup to each Kml Layout.
i tried this for popup but no luck:
const track = new L.KML(kml).bindPupop("text of the popup");
could not figured out how to add onclick.
it it possible with this approach? Thanks!
Related
I am using the canvas element to with request animation frame to give my website an animation that listens to a KeyboardEvent. The issue i'm dealing with is that when I load the page, or refresh the page it depends on the KeyboardEvent to load the fillRect on my canvas, rather than having the fillRect already on the canvas when my website loads or refreshes.
<canvas ref={canvasRef} className="landingPage__snakegame"></canvas>
const LandingPage: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const ctx = canvasRef.current?.getContext('2d');
const [x,setX] = useState(100);
const [y,setY] = useState(50);
const handKeyPress = (event: KeyboardEvent) => {
if (event.key === "w") {
setY(y - 1)
}
}
console.log(canvasRef)
useEffect(() => {
window.addEventListener('keydown',handKeyPress);
return () => {
window.removeEventListener('keydown',handKeyPress)
}
})
const animateSquare = () => {
x + 1
ctx?.clearRect(0,0,canvasRef.current!.width,canvasRef.current!.height);
ctx?.fillRect(x,y,25,25);
requestAnimationFrame(animateSquare);
}
useEffect(() => {
animateSquare();
}, [y]);
I'm fairly sure that the main reason this is happening is because I have HTMLCanvasElement passed in as null, but I am unsure on how to change prevent due to TypeScript restrictions
I have an app that uses event source to get the data from a spring boot backend. The issue I am facing is that when I open the same page in the new tab, the already loaded tab fetches the data again and duplicates the data in the table. I am not sure why that happens, with every tab do we not get an instance of the app and therefore each tab should be its on instance and should initiate a new event source connnection with the back end? The code is shown below:
export default function CurrentShifts() {
const [shiftArray, setShiftArray] = useState<any>([]);
useEffect(() => {
setTableTypeFunc();
const eventSource = new EventSource(baseUrl + "/shifts/getAllShifts", {
withCredentials: true,
});
eventSource.onopen = (event: any) => console.log("open", event);
eventSource.onmessage = (event: any) => {
setShiftArray((oldArray: any) => {
return [...oldArray, JSON.parse(event.data)];
});
};
eventSource.onerror = (event: any) => {
console.log(event);
eventSource.close();
};
return () => {
eventSource.close();
};
}, []);
return (
<div className="table">
<BasicTableButtons dataArray={shiftArray} />
</div>
)
The table in the new tab is shown below:
The table in the original tab is shown below:
in the tomtom docs i find many handlers, but there are no given examples on how to use them, especially in react..say i have this very basic tomtom map component, and i want to handle a user click event to drop a pin/marker on the clicked location..how can i handle the map click event? in react syntax
function MapLocation() {
const mapElement = useRef();
const [mapLongitude, setMapLongitude] = useState(-121.91599);
const [mapLatitude, setMapLatitude] = useState(37.36765);
const [mapZoom, setMapZoom] = useState(13);
const [map, setMap] = useState({});
useEffect(() => {
let map = tt.map({
key: "key here",
container: mapElement.current,
center: [mapLongitude, mapLatitude],
zoom: mapZoom
});
setMap(map);
var mcoords = [-121.91595, 37.36729];
var marker = new tt.Marker().setLngLat(mcoords).addTo(map);
return () => map.remove();
}, []);
return (<div ref={mapElement} className="mapDiv h-100"></div>)
}
I am trying to use Google's model viewer to load 3D models. It has a button. But I want that button to be visible after the model is completely loaded. So, I used to use this Vanilla JavaScript code
<script>
const modelViewer = document.querySelector("model-viewer");
const button = document.getElementById("my_ar_button");
modelViewer.addEventListener("load", function() {
button.style.display = "block";
});
</script>
Now I am planning to use it in ReactJS and this is what it looks like
const modelViewer = document.querySelector("model-viewer");
const button = document.getElementById("my_ar_button");
modelViewer.addEventListener("load", function() {
button.style.display = "block";
});
<model-viewer
src="https://modelviewer.dev/shared-assets/models/reflective-sphere.gltf"
alt="A 3D model of an astronaut"
ar ar-modes="webxr scene-viewer quick-look"
ar-scale="auto"
quick-look-browsers="safari chrome"
ios-src="https://modelviewer.dev/shared-assets/models/Astronaut.usdz"
loading="eager"
poster="https://modelviewer.dev/assets/poster-astronaut.png"
autoplay
camera-controls
>
<button id="my_ar_button" class="my_ar_button" slot="ar-button">Show AR</button>
</model-viewer>
My CSS
.my_ar_button {
display: none;
}
I have added model-viewer using script tag
<Helmet>
<script
type="module"
src="https://unpkg.com/#google/model-viewer/dist/model-viewer.min.js"
async
>
</script>
</Helmet>
But it is giving me this error
TypeError: Cannot read property 'addEventListener' of null
You need to control <model-viewer> element within a React wrapper. See react-model-viewer, this is is their wrapper
const useModelLoader = (type, src): ModelData => {
const loader = useMemo(() => getLoader(type), [type]);
const [model, setModel] = useState(undefined);
const [modelCenter, setModelCenter] = useState<THREE.Vector3>(undefined);
const [error, setError] = useState(undefined);
const [progress, setProgress] = useState(0);
useEffect(() => {
loader.load(
// resource URL
src,
// called when the resource is loaded
model => setModel(model),
// called while loading is progressing
({ loaded, total }) => setProgress((loaded / total) * 100),
// called when loading has errors
error => setError(error)
);
}, [loader, src]);
// get the center of the model
useEffect(() => {
if (!model) {
return;
}
const box = new THREE.Box3();
box.setFromObject(model.scene);
const center = new THREE.Vector3();
box.getCenter(center);
setModelCenter(center);
}, [model]);
return { model, modelCenter, progress, error };
};
The 2nd useEffect() depends on model, so you can do the same with the button. This would be the React equivalent of modelViewer.addEventListener("load", function() {
const [showButton, setShowButton] = useState(false);
useEffect(() => {
if (model) { // when model has a value, the loader has completed
setShowButton(true); // now show the button
}
}, [model]);
const Button = showButton ? <button...> : null; // use this to wrap <button>
In my React-App (create-react-app) I use a selector (created with reselect) to compute derived data from stored data.The Problem is, the selector takes a long time to compute. I would like to show a spinner (or a message) on the user interface. But each time the selector is recomputed the ui freezes.
I read a lot of stuff (Web Worker, requestIdleCallback, requestAnimationFrame) and try to make my own React hook but I am stuck. I cannot use the selector in callbacks.
The searched result is simply to get the ui refreshed before the selector is recomputed.
That's my solution. I don't know if it's "good" or if it breaks some rules of react or reselect, but it works. Maybe you can assess that?The code is simplified to improve readability.
The idea is, the selector returns a Promise and I call requestAnimationFrame before computing the data to get a chance to refresh the ui.
selector.js:
const dataSelector = state => state.data;
export const getDataComputedPromise = createSelector([dataSelector], (data) => {
const compute = function () {
return new Promise((resolve) => {
// heavy computing stuff
resolve(computedData);
});
};
return new Promise((resolve) => {
let start = null;
let requestId = null;
function step (timestamp) {
if (!start) {
start = timestamp;
window.cancelAnimationFrame(requestId);
requestId = window.requestAnimationFrame(step);
return;
};
compute().then(freeResources => {
window.cancelAnimationFrame(requestId);
resolve(freeResources);
});
}
requestId = window.requestAnimationFrame(step);
});
});
myPage.js
const MyPage = ({ someProps }) => {
...
const dataComputedPromise = useSelector(getDataComputedPromise);
const [dataComputed, setDataComputed] = useState([]);
const [computeSelector, setComputeSelector] = useState(false);
useEffect(() => {
setComputeSelector(true);
}, [data]);
useEffect(() => {
dataComputedPromise.then(dataComputed => {
setDataComputed(dataComputed);
setComputeSelector(false);
});
}, [dataComputedPromise]);
...
return <div>
<Loader active={compueSelector}>Computing data...</Loader>
</div>;
};
export default MyPage;