Assigning ref in react-konva - reactjs

How do we assign ref to the stage or layer object. I am using react-konva. When I do console.log(stageE1), it says undefined.
useEffect(() => {
var stage = new Konva.Stage({
container: 'stage',
width: window.innerWidth,
height: window.innerHeight,
ref: stageEl, // not working
onMouseDown: (e) => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage()
if (clickedOnEmpty) {
selectShape(null)
}
},
})
stage.container().style.border = '1px solid grey'
var layer = new Konva.Layer({
ref: layerEl, // not working
})
stage.add(layer)
}, [])

This is not react-konva usage. You are using Konva API directly. If you do this, you probably don't need to use refs. But if you really want:
var layer = new Konva.Layer();
// set ref
layerEl.current = layer;
If you use react-konva, then you should define components in React way:
import { Stage, Layer } from 'react-konva';
const App = () => {
const layerEl = React.useRef();
return <Stage><Layer ref={layerEl} /></Stage>;
};

Related

How to make dynamic changes in Arcgis JS map-component with React?

I am quite new to Arcgis-JS and React. As suggested here, I am using a functional component with the useEffect hook to integrate my map.
Now I want to display a line within my map when I click on a certain row of a list. On click I am fetching the appropriate coordinates to be displayed and storing them to a context-variable (dbPageContext.currentGeom).
The problem: When I want to display another line, the entire map-component has to re-render as I am passing the line-array-variable as a second argument to the useEffect hook.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const graphicsLayer = new GraphicsLayer();
mainMap.add(graphicsLayer);
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
graphicsLayer.add(polylineGraphic);
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
return () => {
view && view.destroy();
};
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;
How can I update only the graphics-layer without updating the entire map-component? Would be great if someone could help me finding a solution for that.
EDIT: I also tried to implement the map without using the useeffect hook. But then, nothing was displayed.
You need to separate the effects. On page load, you should have one effect that creates the map. Then a second effect can update the map when dbPageContext.currentGeom changes.
const MapComp = () => {
const mapRef = useRef();
const dbPageContext = useContext(DbPageContext);
// Memoize this, as you only need to create it once, but you also need
// it to be available within scope of both of the following useEffects
const graphicsLayer = React.useMemo(
() => new GraphicsLayer(),
[]
);
// On mount, create the map, view, and teardown
useEffect(() => {
const mainMap = new Map({
layers: [layer],
basemap: "arcgis-topographic", // Basemap layer service
});
const view = new MapView({
container: mapRef.current,
map: mainMap,
spatialReference: {
wkid: 3857,
},
});
mainMap.add(graphicsLayer);
return () => {
view && view.destroy();
};
}, [])
// When dbPageContext.currentGeom changes, add a polyline
// to the graphics layer
useEffect(() => {
const polyline = {
type: "polyline",
paths: dbPageContext.currentGeom,
};
const simpleLineSymbol = {
type: "simple-line",
color: [0, 230, 250],
width: 4,
};
const polylineGraphic = new Graphic({
geometry: polyline,
symbol: simpleLineSymbol,
});
// Clear previously added lines (if that's what you want)
graphicsLayer.removeAll()
graphicsLayer.add(polylineGraphic);
}, [dbPageContext.currentGeom]);
return (
<div>
<div className="webmap" ref={mapRef} />
</div>
);
};
export default MapComp;

useRef type on React Draggable

I am relatively new to coding and especially to Typescript. I am trying to create a React Draggable modal that changes its bounds when the window gets resized and move the modal with it, so it never gets out of the window. I have created a function that does that but I am struggling to use the correct type for the ref draggableRef that is used on the actual draggable. What type is the draggableRef? Whatever I change it to there is problem with the .state.x; on the modalOffsetLeft that it doesn't exist on it.
Or, is there other way to do it? Thanks!
here is the code:
export const DraggableModal: React.FC<DraggableModalProps> = ({
draggableProps,
onClose,
}) => {
const [bounds, setBounds] = useState({
width: 0,
height: 0,
});
const ref = useRef<HTMLDivElement>(null);
const draggableRef = useRef<DraggableCore>(null);
const rootWindow = document.getElementById('root') as HTMLDivElement;
const getValues = () => {
if (ref.current !== null && draggableRef.current !== null) {
const modalWidth = ref?.current?.clientWidth;
const draggableWindowWidth = rootWindow?.clientWidth - modalWidth;
const modalOffsetLeft = draggableRef?.current?.state.x;
setBounds({
width: draggableWindowWidth,
height: rootWindow?.clientHeight - ref.current.offsetHeight,
});
if (modalOffsetLeft > draggableWindowWidth) {
draggableRef.current.state.x = draggableWindowWidth;
}
}
};
useEffect(() => {
getValues();
}, []);
parent.onresize = getValues;
return (
<Draggable
ref={draggableRef}
bounds={{
left: 0,
top: 0,
right: bounds.width,
bottom: bounds.height,
}}
{...draggableProps}
handle="#handle"
>
<DraggableWrapper>
...
</DraggableWrapper>
....
instead of updating its x,y using ref try to create a state {x,y} and then update this x and y.
then pass x and y to position props in Draggable.
by the way you can run the function get values in the more clearing way by adding the function to a listner that runs when the window is changes.

How to query routes in here maps with reactjs

I am trying to make map queries with here maps on routes with reactjs, currently I can display the map in my application with this code:
import * as React from 'react';
export const DisplayMapFC = () => {
// Create a reference to the HTML element we want to put the map on
const mapRef = React.useRef(null);
/**
* Create the map instance
* While `useEffect` could also be used here, `useLayoutEffect` will render
* the map sooner
*/
React.useLayoutEffect(() => {
// `mapRef.current` will be `undefined` when this hook first runs; edge case that
if (!mapRef.current) return;
const H = window.H;
const platform = new H.service.Platform({
apikey: "xxxxxxxxx"
});
const defaultLayers = platform.createDefaultLayers();
const hMap = new H.Map(mapRef.current, defaultLayers.vector.normal.map, {
center: { lat: 36.2104063, lng: -113.7236071 }, //,
zoom: 4,
pixelRatio: window.devicePixelRatio || 1
});
const behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(hMap));
const ui = H.ui.UI.createDefault(hMap, defaultLayers);
// This will act as a cleanup to run once this hook runs again.
// This includes when the component un-mounts
return () => {
hMap.dispose();
};
}, [mapRef]); // This will run this hook every time this ref is updated
return <div className="map" ref={mapRef} style={{ height: "355px",width:"650px" }} />;
};
const searchMap=(e)=>{
e.preventDefault();
console.log(" view controle ",e);
const origin="52.5308,13.3847";
const destiny="52.5264,13.3686";
}
I am trying to send coordinates from a start point to an end point so that I can show the plotted route and time
this worked for me:
Change API_KEY, origin and destination. Luck!
https://codesandbox.io/s/react-with-heremap-2cze0?file=/src/mapHere.js

What is the right way to bind Highcharts event in React functional Component?

I am using highcharts in react with react highchart:
demo
I defined the chart option in another file and import it as a state
import * as options from "./chartConfig";
const [option, setOption] = useState(options.option_bubble);
and now change the option in useEffect,why I have to do it in react component is because I need to do further things like fetch data so I can't just define it in the chartConfig.js.
Now mycode is like:
export default function HighChart(props) {
const [state1, setState1] = useState("button");
const [option, setOption] = useState(options.option_bubble);
const [refreshTrigger, setRefreshTrigger] = useState(0);
const clickChart = event => {
console.log("event", event.point);
};
useEffect(() => {
console.log("useEffect");
option.plotOptions.series.point.events.click = clickChart;
let NewOptoin = Object.assign({}, option);
setOption(NewOptoin);
setRefreshTrigger(Math.random);
},[props]);
so I want to bind the render() inside react component. in pure js it looks like this:
console.log(Highcharts.chart);
Highcharts.chart('container', {
chart: {
type: "bubble",
plotBorderWidth: 1,
events: {
render() {
let chart = this;
chart.series[0].points.forEach(p => {
let labelWidth = p.marker.width,
dataLabel = p.dataLabel,
padding = 2;
dataLabel.css({
width: labelWidth
})
if (isFirstRender){
ÏdataLabel.translate(dataLabel.alignAttr.x + dataLabel.getBBox().width / 2 - padding, dataLabel.alignAttr.y)
isFirstRender = false;
}
})
}
}
},
so what render function is in the events object, why it is not looks like:
events:{
render:function(){
}
}
and how can I re-define it in react ?

Accessing values from an eventListener inside a React Hook with an empty dependancy array

I am attempting to use React hooks to run a canvas animation that depends on mouse position. I am using a custom hook for mouse position, and wrote another custom hook to animate the canvas.
Placing an empty dependancy array in the animation hook keeps it from unmounting and remounting the animation loop anytime the mouse moves, as is mentioned in this tutorial and suggested in a note in the React docs.
So the code below works, in that I can access coords inside the drawNow() function, but mounting and unmounting the animation loop every time the mouse moves does not seem like an acceptable way to do things.
How does one access event listeners inside React hooks that are purposely set to have no dependancies?
Here is the animation and the draw function....
const drawNow = (context,coords) => {
context.fillStyle = '#fff';
context.beginPath();
context.arc(coords.x,coords.y,50,0,2*Math.PI); // need coords here
context.fill();
}
export const Canvas = () => {
let ref = React.useRef();
// custom hook that returns mouse position
const coords = useMouseMove();
React.useEffect(() => {
let canvas = ref.current;
let context = canvas.getContext('2d');
const render = () => {
aId = requestAnimationFrame(render);
drawNow(context,coords); // requires current mouse coordinates
};
let aId = requestAnimationFrame(render);
return () => cancelAnimationFrame(aId);
}, [coords]); // dependancy array should be left blank so requestAnimationFrame mounts only once?
return (
<canvas ref={ref}/>
style={{
width: '100%',
height: '100%',
}}
);
};
Here is the custom hook for the mouse coordinates (references this useEventListener)
export const useMouseMove = () => {
function getCoords(clientX,clientY) {
return {
x: clientX || 0,
y: clientY || 0
};
}
const [coords, setCoords] = useState(getCoords);
useEventListener('mousemove', ({ clientX, clientY }) => {
setCoords(getCoords(clientX,clientY));
});
return coords;
};
Thanks, and looking forward to understanding more about hooks and event listeners.
Okay, I figured out my problem. The issue is the useMouseMove() hook was updating the coordinates with useState, when really I wanted to be using useRef, allowing me to imperatively "modify a child outside the typical data flow", as mentioned here.
First, I combined the useMouseMove() hook with the more general useEventListener so I didn't have to navigate unnecessary abstractions:
// a function that keeps track of mouse coordinates with useState()
export const useMouseMove = () => {
function getCoords(clientX,clientY) {
return {
x: clientX || 0,
y: clientY || 0
};
}
const [coords, setCoords] = useState(getCoords);
useEffect(
() => {
function handleMove(e) {
setCoords(getCoords(e.clientX,e.clientY));
}
global.addEventListener('mousemove', handleMove);
return () => {
global.removeEventListener('mousemove', handleMove);
};
}
);
return coords;
};
The next step was to "convert" the above function from useState() to useRef():
// a function that keeps track of mouse coordinates with useRef()
export const useMouseMove = () => {
function getCoords(clientX,clientY) {
return {
x: clientX || 0,
y: clientY || 0
};
}
const coords = useRef(getCoords); // ref not state!
useEffect(
() => {
function handleMove(e) {
coords.current = getCoords(e.clientX,e.clientY);
}
global.addEventListener('mousemove', handleMove);
return () => {
global.removeEventListener('mousemove', handleMove);
};
}
);
return coords;
};
Finally, I can access the mouse coordinates inside the animation loop while also keeping the dependancy array blank, preventing the animation component from remounting every time the mouse moves.
// the animation loop that mounts only once with mouse coordinates
export const Canvas = () => {
let ref = React.useRef();
// custom hook that returns mouse position
const coords = useMouseMove();
React.useEffect(() => {
let canvas = ref.current;
let context = canvas.getContext('2d');
const render = () => {
aId = requestAnimationFrame(render);
drawNow(context,coords.current); // mouse coordinates from useRef()
};
let aId = requestAnimationFrame(render);
return () => cancelAnimationFrame(aId);
}, []); // dependancy array is blank, the animation loop mounts only once
return (
<canvas ref={ref}/>
style={{
width: '100%',
height: '100%',
}}
);
};
Thanks to this escape hatch, I am able to create endless web fun without remounting the animation loop.

Resources