OnClick for mapbox Layer - javascript

I am using react-map-gl to add a map to my React app. I want to add an onClick event to my Layer component, but it looks like react-map-gl does not support it. (docs here: https://visgl.github.io/react-map-gl/docs/api-reference/layer )
I've recently discovered that react-mapbox-gl does support onClick events for Layer components, but I am having trouble installing it through npm (tree dependency issues).
I was wondering if anyone here has managed to add the onClick event using react-map-gl? Any advice?
Example code below:
import React from "react";
import { Layer, LayerProps } from "react-map-gl";
export const MapLayer: React.FunctionComponent<LayerProps> = ({
id,
type,
paint,
source,
layout,
}) => {
return (
<Layer id={id} type={type} paint={paint} source={source} layout={layout} />
);
};
Thanks,
Robert

In fact, react-map-gl would definitely support onClick event.
However, the event is not designed to respond to each individual layer, instead it comes under the default Map component. You will need to specify the interactiveLayerIds property to make it work on specific layers.
Please refer to the section of onClick event from react-map-gl documentation.

Related

How to use d3 component in react without breaking its animation

I was trying out d3 sunburst component from here. I wanted to use it in react. To use it in react, we have to use another library called react-kapsule:
import SunburstChart from "sunburst-chart";
import fromKapsule from "react-kapsule";
const ReactSunburst = fromKapsule(SunburstChart, {
methodNames: ["onClick"]
});
<ReactSunburst
width={200}
height={200}
label="name"
size="size"
data={flare}
onClick={(entry) => {
console.log("Hello from inside onClick handler!!");
}}
/>
It renders as follows:
The problem is with specifying custom onClick handler. When I specify methodNames: ["onClick"], clicking on slice of sunburst chart zooms in that slice, but it does not log the message to the console. codesandbox link
If I remove onClick from methodNames, it logs the message to method names, but it does not zooms in the slice. codesandbox link
How I add working onClick handler while at the same time ensuring that the component's default behavior wont break?

WMSTileLayer is not updating after changing the layers parameter in react-leaflet

I am trying to update my WMS layer on react-leaflet v2.x.x. The WMSTileLayer works only with the first layer and when I change the layers parameter on it, it shows the previously added layer. I have written following code,
<MapContainer
whenCreated={(mapInstance) => {
mapRef.current = mapInstance;
}}
center={[38.861, 71.2761]}
zoom={5}
zoomControl={false}
style={{ width: "100%", height: "70vh" }}
>
<ZoomControl position="topright" />
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution="© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
/>
<WMSTileLayer
layers={this.props.wmsLayer}
url={`${process.env.REACT_APP_GEOSERVER_URL}/wms`}
transparent={true}
format={"image/png"}
/>
</MapContainer>
So my question is, how to update the WMSTileLayer when my this.props.wmsLayer is updated?
You are using the MapContainer component, which makes me suspect you are actually using react-leaflet version 3. In react-leaflet version 3, many props are immutable, meaning once they are set, changing them from a react props perspective will do nothing. You would need to get a ref to the underlying L.TileLayer.WMS and call a method on it to change the layers. Unfortunately, the leaflet docs do not seem to show any methods to do that (i.e. like a setLayers function or something like that). You could call setUrl on the tilelayer, though I'm not sure that's what you want.
The easiest thing to do would be to add a key prop to your WMSTileLayer, and set it to some unique id that will force react to rerender when this.props.wmsLayer changes. I'm not sure what this.props.wmsLayer is, but if it has some unique url or id associated with it, set key={this.props.wmsLayer.id} or whatever you can, and react will force rerender that component. It's a bit overkill, but without some kind of setLayers function in the leaflet api, I'm not sure that there's a more proper way to do it.

React 16.9.0 "javascript:;" href alternative? [duplicate]

This question already has answers here:
In React, how can I cause anchors to do nothing on click?
(17 answers)
Closed 3 years ago.
After updating to React 16.9.0 I'm getting big warnings like this:
Warning: A future version of React will block javascript: URLs as a security precaution. Use event handlers instead if you can. If you need to generate unsafe HTML try using dangerouslySetInnerHTML instead. React was passed "javascript:;".
It comes from code like this:
const Component = ({someAction}) => (
<a href="javascript:void(0)" onClick={someAction}>click me</a>
);
Looking at the StackOverflow question about which “href” value should I use for JavaScript links in HTML it seems most of you agree javascript:void(0) is the best option, which will be no longer possible in React 16.9.0.
Just replacing with href="#" is problematic, since the browser will scroll to the top of the page and change the displayed URL. Especially if you use hash-links for routing this is very problematic.
I could update my whole codebase to have e.preventDefault(); in each and every event handler, but this seems hard to do, especially when the event handlers are automatically created from Redux action creators or hooks. I do not look for the answer "just include e.preventDefault();" everywhere!
Also using a <button> means I have to deal with lots of unwanted styles applied.
So I was wondering: Are there any solutions specific to the library React to get a working <a> link that just triggers an action without side effects? I want to change the code as little as possible and get rid of deprecation warnings.
In React blog post:
URLs starting with javascript: are a dangerous attack surface
because it’s easy to accidentally include unsanitized output in a tag
like <a href> and create a security hole.
In React 16.9, this pattern continues to work, but it will log a
warning. If you use javascript: URLs for logic, try to use React event
handlers instead.
I personally prefer to use it like this:
<a href="#!" onClick={clickAction}>Link</a>
I came up with a component like this:
import React, {useCallback} from 'react';
function AHrefJavascript({ children, onClick, ...props }) {
const handleClick = useCallback(
e => {
e.preventDefault();
return onClick(e);
},
[onClick]
);
return (
<a href="#javascript" {...props} onClick={handleClick}>
{children}
</a>
);
}
It will just wrap the event handler to create a new one that also calls e.preventDefault(). It will add a hash link to href which does not trigger the deprecation warning. Using the power of hooks it only changes the event handler, when the passed in handler is updated. One problem with this that in the browser you can still open this link in a new tab, so it is not quite as good as a link with a javascript:void(0), which would prevent that.
It is easy to use, the component looks like this:
const Component = ({someAction}) => (
<AHrefJavascript onClick={someAction}>click me</a>
);
So probably one could replace a href="javascript:void(0)" with AHrefJavascript across the project and then just add an import for the component everywhere.
In the end maybe the best option is to use a <button> and go through the hoops to remove all the unwanted styles.

Why does React warn against an contentEditable component having children managed by React?

I get the following warning when rendering my component:
Warning: A component is contentEditable and contains children
managed by React. It is now your responsibility to guarantee that none
of those nodes are unexpectedly modified or duplicated. This is
probably not intentional.
This is my component:
import React, { Component } from "react";
export default class Editable extends Component {
render() {
return (
<div contentEditable={true} onBlur={this.props.handleBlur}>
{this.props.children}
</div>
);
}
}
What is the potential problem with my code that React wants to warn me about? I did not quite understand from reading the documentation at https://reactjs.org/docs/dom-elements.html.
I imagine that my component should work exactly like an managed input field, without any problem:
this.props.children is initial value
the onBlur callback updates the props from event.target.innerHTML
the component is rendered with the new props
Setting the contenteditable html attribute allows the contents of that element to be modified in the browser. React is warning you that you have children within that element that are managed by React. React only works from the top down. Meaning it manages a model at the top level and maintains a virtual DOM representing that data, then renders the DOM tree based on that virtual DOM. Any changes you make to the DOM outside of React (such as setting contenteditable and allowing the content to be edited by a user directly in the browser) will be potentially blown away or cause problems for React when it goes to update those managed elements.
In your situation you don't care that the {this.props.children} node gets blown away because you know you're catching the changes and doing what you need to with it. It's just warning you that you better not expect that node to remain intact and accurately updated by React when you're letting the content be edited by the browser directly.
If you know what you're doing (and for now it looks like you do) then you can suppress that warning by adding suppressContentEditableWarning={true}.
Thanks #Chev! It fixed the warnings..
<p
className={editing ? 'editing' : ''}
onClick={editOnClick ? this.toggleEdit : undefined}
contentEditable={editing}
ref={(domNode) => {
this.domElm = domNode;
}}
onBlur={this.save}
onKeyDown={this.handleKeyDown}
{...this.props}
suppressContentEditableWarning={true}
>
{this.props.value}
</p>

How can I get React to Replace (not Merge) VDOM?

I have a component which, in componentDidMount, gives a jQuery plugin some control over the DOM rendered by React. I know everyone says "never let anything but React touch the DOM", but hear me out, as reinventing this plugin is not feasible right now, and I think there should be an "overwrite whatever you find in the DOM" switch for React that I hope someone can point me to.
More info: I've designed it so the state of the React's DOM is entirely determined from the props given to React except while the user is dragging things around. Once dropped, I don't care how the DOM changed since the last React update, I just want to render everything from the current props of React, which I am passing in on the plugin's change handler via ReactDOM.render
The symptoms are that the nodes created by the plugin during and after dragging don't go away after React is told to update!
Yes, the nodes are key-ed initially.
The plugin is Nestable, and it adds interactivity (drag-drop reordering of the tree), and a JSBin is here: http://jsbin.com/qareki/edit?js,console,output
I'm really looking for the "Kill whatever you find" setting. I thought calling ReactDOM.render would do it, but it's clearly not doing it. Neither of course, was the more surgical setState, but I didn't expect it to. Thanks in advance for all 'you're doing-it-wrong' advice and other fixe
Manually add a div element in componentDidMount and replace it with a new one in componentDidUpdate:
class Foo extends React.Component {
render() {
// whatever HTML you want...
return (
<div>
<div>
{/* this div will contain our non-React stuff that we need to reset */}
<div ref="container"></div>
</div>
</div>);
}
blastAndRecreate() {
// throw away any content within the container and replace it with brand new content
const container = $(this.refs.container).empty();
const newDIV = $("<div>").appendTo(container);
// give this new DIV to nestable plugin
newDIV.nestable(...);
}
componentDidMount() {
this.blastAndRecreate();
}
componentDidUpdate() {
this.blastAndRecreate();
}
}

Categories