I know this is the proper way to do -
Attach handlers on react components using attributes such as onClick, onBlur etc.
If attaching custom events then attach them in componentDidMount so that it always gets attached on every rendering.
https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#under-the-hood-autobinding-and-event-delegation
However, what happens when an event is attached to a DOM element using jquery ? Why am I not able to listen to those events ? I am attaching them out side componentDidMount. However the event handler should get trigerred the first time atleast ?
I was debugging some react code which was attaching an handler to it directly, but it was not trigerring the handler at all. Why was that ?
Without code example it's difficult to know for sure but more than likely you're trying to bind an event using jQuery to some react-generate DOM element. So depending where you do the bind, you're probably using a jQuery selector which returns no results because React hasn't rendered that element to the DOM at the time the selector is being run.
This is why, if you are going to use jQuery binding, you should make sure you do so as part of the component lifecycle that ensures the element is actually rendered to the DOM before you are trying to select it with jQuery. So that's why the recommendation is to put this kind of code inside componentDidMount and clean it up in componentWillUnmount.
Related
I have a map within my react project where I want to use a click listener on the base map to find the coordinates of the point clicked. It has worked fine for a while now.
The problem is that I've added a few layers using clusters, some of which have click listeners, and now clicking anywhere on the map gives me the error "There is already a source with this ID." I think I get why - there's a click, but not on any of the layers.
I can't find any information on how to make the base map a layer, and thereby get an id for it to pass to the click listener. So my question is, how to I attach a click listener to the base map while still using layers that may also have click listeners, too.
So that was a big pain! What was happening was that I was passing a function down to the component that contained the click listener and that function changed the state of the parent component. Given that this is react, it caused the child components to rerender and thus Mapbox to error that there was already a source with the same id because the source had never been removed.
The solution was to wrap the code that added the source and attendant layers as a component, then create a cleanup component that contained removeSource (see here) and run that on unmounting. I'm using the new hooks api, so I'm running a setLayers function in the useEffect hook, then returning cleanup from useEffect, but I bet this sort of idea would work fine with componentDidMount and componentWillUnmount.
This is from a couple React tutorials that I'm currently reading:
the State drives what the guys at Facebook call one-way reactive data
flow, meaning that our UI will react to every change of state.
and
Typically UI’s have lots of state which makes managing state
difficult. By re-rendering the virtual DOM every time any state change
occurs, React makes it easier to think about what state your
application is in. The process looks something like this, Signal
to notify our app some data has changed→ Re-render virtual DOM ->
Diff previous virtual DOM with new virtual DOM -> Only update real DOM
with necessary changes.
The first quote seems to suggest that the data flow goes from React to the UI. But the second quote seems to suggest that it goes from the DOM to React which then re-renders the virtual DOM and the diff process than repaints the real DOM. This sounds a lot like Angular's two-way data binding.
Is this true? What am I missing? Is one-way reactive data flow just another name for Angular's two-way data binding?
I think it's necessary to make a distinction between React and Flux, both of which implement a uni-directional data flow. I'll only touch on React here.
In Angular a change to the DOM will directly mutate the value of bound scope variables in your controller thanks to angular's two-way data binding and the digest loop. It is two way because, of course, any change in your controller is also directly reflected in the view attached to it.
Think of Angular as Controller <--> View
In React a component always renders its DOM based on its props and internal state. A React component can listen to event being fired in the DOM it rendered, which might sound a bit like two-way data flow on the face of it but there is an important difference: An event fired on the DOM does not directly update the internal state of the component. The component must listen to the event and then explicitly update the component with the new state. This change of state then triggers the DOM to be re-rendered (if it needs to). In this way the components props and state are the single source of truth and always drive the DOM output. Of course changes in the DOM can affect the component but it is done in an indirect manner.
Think of React as Component State --> DOM --> New Component State --> New DOM
Angular's two way of binding's essence is that the view is bound both to the model and the user's input.
With React, the view is bound only to the state and a user's input cannot directly change the view, but triggers the component's own methods to update its state and Reacts job is to rerender the view so it reflects the state.
The flow here is:
Component
state-> virtual DOM -> DOM
This is always the flow, whether its initial render or second render.
The quoted bit is:state->virtual DOM -> DOM
newStateDifferentFromOldState -> virtual DOM
diff virtual DOM from 1 with virtual DOM from 2
Only update the elements of the DOM that are the net difference of performing 3.
e.g. 1->2->3->4, repeat in this order from 2 (2->3->4->2->3->4...so on)
This is in no way related to the concept of two way data binding.
This may not be a Mithril specific question but VM DOM diffing in general. While "redrawing" a page and you attach a new event handler, remove an event handler, or change the event handler associated with an event such as "onclick", it seems that the VM DOM diffing will not detect the change and I have to explicitly force a rerendering of the "real" DOM.
Is there a good pattern to handle these situations.
In Mithril specifically, onclick and all the on-handlers automatically trigger a redraw after the handler function has completed. However, if your onclick handler does real DOM manipulation (3rd party libraries such as jQuery or an animation suite) you need to use the config attribute:
http://lhorie.github.io/mithril/mithril.html#the-config-attribute
which gets called after the page has been rendered.
With regards to changing event handlers, it depends on when in the render cycle you make the change. I'd have to see some code. If it's not too inconvenient, calling redraw manually is not a bad thing. If the redraw strategy is "diff" you won't pay a penalty.
And lastly, if you are introducing your own events, consider using m.startComputation/m.endComputation in your event handlers. These provide much finer grain control and a redraw is automatically called.
http://lhorie.github.io/mithril/mithril.computation.html
I'm using react with a map library, I use react to render elements in a container which provided by the map library.
The problem is that the container prevent event bubbling, I checkout reactjs source code found the event emitter listen on html document and depend on event bubbling to dispatch event.
How can I make the onClick handler to work? I currently addEventListener manually but don't think it a elegant way.
<body>
<container preventBubbling>
<ReactElement onClick={this.handleClick}/>
</container>
</body>
As you said, React depends on event bubbling instead of attaching event handlers directly to any DOM node you want events for. This is an integral part of React, and not something you can avoid without manually attaching event handlers in componentDidMount. And also manually removing them in componentWillUnmount.
What's the reason for cancelling bubbling in the map library? I'd try to remove that cancellation and see if it works.
I saw that it adds events to an Observable. However, you would only add it if you would fire it at some point. So, if you add the firing of the event at some line in your code, it was useless to put it into addEvents in the first place and when you don't place the fireEvent in your code then it was pointless to include it in addEvents. What am I missing?
If you are adding custom events with your component then you must add the events with addEvent before you can fire the events with fireEvent. Adding and firing events is useful in binding custom components together without explicitly referencing them.