React Hooks : UseRef not recognizing Event from Material UI Select - javascript

I'm using React Hooks.I am trying to trigger a onclick event using useRef.
const component1: React.FC<Props> = props {
const node =useRef<HTMLDivElement>(null);
const ClickListener = UseCallback((e:any)=>{
if(node.current.contains(e.target))
console.log("Event Contained");
else console.log("Event not Contained");
},[]);
return (
<div ref={node} onClick={ClickListener}>
<FormControl>
<InputLabel>{text}</InputLabel>
<Select> {contents} </Select>
</FormControl>
</div>
);
};
I have this Component1 called by componentX and componentY.
The above code is failing to recognise onclick event on a componentX's node when componentY's select menu is opened.
Only after closing ComponentY's select menu and clicking again on ComponentX currently recognises the event.
Any Suggestions on why itsn't recognized.I am using Material UI's select.

Use callback ref.
Quoting from API doc:
Keep in mind that useRef doesn’t notify you when its content changes.
Mutating the .current property doesn’t cause a re-render. If you want
to run some code when React attaches or detaches a ref to a DOM node,
you may want to use a callback ref instead.
Edit
Sorry to lead you in wrong direction. My suggestion to use callback ref is not correct for your code.
Your callbacks are working correctly and are able to use the div ref properly.
The reason you feel your callback is not working when Select in other component is opened is that Select uses mouse capture to detect click outside the pop-up.
So when your drop-down is open, click on other component div does not reach that div at all, it is consumed by Select. Subsequent click works well.
This is the case not only with your component but with any other element in the dom.
See: https://codesandbox.io/s/silly-star-k430x

You should pass node in the dependancy list to the useCallback method.
Otherwise, the node which refers to null while defining the ClickListener is not updated.
Go through the docs - useCallback. useCallback avoids redefining the function every time the component function is called. So any global variables inside the function definition has to be listed in dependancies array, so that react will redefine the function when ever the dependancies change.
const ClickListener =UseCallback((e:any)=>{
if(node.current.contains(e.target))
console.log("Event Contained");
else console.log("Event not Contained");
},[node]);

Related

React passing onClick as props to sub components required?

I am trying to trigger an onClick function call inside a sub-component by adding the onClick to the parent component. It does not work.
// parent component
class MainForm extends Component {
// code here
handleClick = () => {
console.log('clicked!');
}
render {
return (
<Linkbutton
text="Click Me!"
onClick={this.handleClick}>
/>
);
};
// sub component
export const LinkButton = (props) => {
return (
<button>{props.text}</button>
);
};
This did not work. The onClick function was not being called on click. I managed to fix it by adding the onClick call in the sub-component as well.
// sub component
export const LinkButton = (props) => {
return (
<button onClick={props.onClick}>{props.text}</button>
);
};
This worked. My question is why? Shouldn't the onClick be called on any element regardless of what is inside it? Why did I have to explicitly pass it down to the sub-component as well?
I would like to understand the reason for it being this way and why it wouldn't work the first way I tried. Thanks!
The reason is that the LinkButton component you created is just a javascript object, not a DOM node, so it does not have event handlers like onClick. The button element is transformed into the actual DOM element so it will have all the associated event handlers that the actual HTML element.
When you add an onClick prop to the LinkButton component, it is just a property of an object. By calling props.onClick from inside of that component you are just calling a function that is stored inside of a property, similar to this:
let props = {
onClick: function () { alert("Executed!"); }
};
props.onClick();
Hope this helps you!
In your first example, you are not actually adding the onClick event. Just passing the reference to the component doesn't mean it automatically know what to do with it.
The second example does actually add the onClick to the button, like it should, which is why it works.
In your first example, you are using "onClick" as a parameter, not as the actual event handler.
you are in little confusion. onClick props you have passed in Linkbutton component doesn't trigger your function call. in react we use props in order to communicate between components. onClick is an event in button element while onClick is props in Linkbutton component.
Unfortuantely, there is no way around that in pure React.js. If you want the parent element's function to handle click event performed on a child element, you have to explicitly pass that function as a prop to the child element.
I understand why it may seem frustrating at times, especially if you have got a tall component tree and numerous leafs of that tree that have to call a method of parent, parent of the parent, or even parent of the parent of the parent [ ... ] element.
One way to mitigate that problem (especially when your project grows big) would be to use a state store that implements the Flux pattern. Good example of that would be Redux. Instead of calling a method passed from the parent component as a prop (such method would in most cases alter the sate of the parent component), your button component would dispatch an action that would then be taken care of by a reducer function, consequently updating the global state of the application. All components of the app that rely on the part of the global state that got changed would then be updated accordingly. In this example, the button component would not communicate with the parent element directly. It would just dispatch an action.
Here is a great tutorial that you should read if you decide to learn Redux. Bare in mind that Redux is commonly considered to be somewhat difficult to start with, therefore it's best suited for big apps where initial increase in project complexity caused by the addition of Redux to the app is outweighed by added ability to avoid all the problems resulting from direct communication between components.

Call a function from one polymer component to another

I use Polymer, Html and Javascript.
I want to find any easy way to call a function from one polymer component to another.
Let me describe what I want to achieve.
I have polymer component myComponent. In this component container with button called start:
<button on-click="_start" id="start">XyZ</button>
and also second component (let's say it is child component, because it is in myComponent, where I have got function _addNew in <script> section.
Now I want to fire _addNew function by clicking button with #start id.
My question is how can I call a function from one polymer component when this function is placed in another?
I hope you understand me.
if I understood correctly, basically what you want to do is fire from one component and listen in the other one.
So you fire from myComponent, in the _start method like:
this.fire('add:new');
and then to listen to the add:new event in your other component by adding a listener, for this specific event name:
listeners: {
'add:new': '_addNew'
},
if it's not explicit enough, please do read the Polymer documentation it's a very nice and well explained documentation.
Hope it helps.
In my case this is not working, because this.fire is going "up", to the parent component, not to a child component.
In my case this one help:
this.$$('component-name').function-name();

How to bubble up an event to the very top parent React style?

I have a set of components app->page->list_container->list->item
My goal is to notify the app that click happened on item level.
If there is simple relation like parent->child I could use props and do something like: <Child onClick={this._onClick}> ... and then use this.props.onClick() to make a callback.
But what is the best native React-style receipt for doing the same trick with a tree of components? How to notify the app, that item was clicked without calling to Flux/Reflux, Elm and other supported libs?
Standard react way:
Passing onClick function as a prop down your component tree is the standard react-way of doing this.
In <app>:
<page onClick={this._onClick}>
In <page>:
<list_container onClick={this.props.onClick}>
Etcetera.
Or you could use:
<list_container {...this.props}>
To automatically pass down any prop from parent component to the child component.
In a deep tree, this can and will get quite tedious/ lot of work. React was not designed for this purpose.
React is made for (top-down) smart and fast component-tree rendering.
The frameworks for the flux pattern you mention are designed to handle the other interactive functions.
Alternative shortcut (not recommended):
A shortcut you could possibly use is to add a listener directly on the DOM, inside your <app> component, that handles the click event on the item:
In <app> component, add:
componentDidMount: function() {
var itemElementInDOM = document.getElementById('myItem');
itemElementInDOM.addEventListener('click',this._onClick);
}
And in your <item> component, give the item a (unique) id.
I would generally NOT recommend this:
In a typical react tree setup, the lower level components (like
<item>) may be rendered more than once, and then you would need
additional logic to ensure that each ID is unique.
You would also
need to add some additional smarts to make sure you remove the listener if the
item(s) in question are removed from the DOM.

Adding event listener to large amount of elements in React

I had a CategoryPicker component which displays a large category tree(hierarchical, about 20,000 nodes), each of the node was bound to a onClick event handler in order to figure out which node the user is clicking.
It turns out to be extremely slow when mounting this component, profiling result shows that EventPluginHub.putListener consumes most of the resources.
The only solution I can think of is using jQuery to bind event handler to it's ancestor, you know, the old school way. But apparently this is against React's design philosophy.
const CategoryPicker = React.createClass({
render() {
return (
// data has about 20,000 elements
{data.map((d) => {
return (
<div onClick={this.handleClick.bind(this, d.cateId)} key={d.cateId}>
{d.cateName}
</div>
);
});}
);
}
});
Update
I've tried remove onClick event handler on the nodes, the performance boost is evident. Guess I have to use the jQuery way to handle this situation right now.
Ideally you should be attaching the event handler to a parent component (just using react, no need for jquery) and let these events bubble up. You can then use the passed event object to determine which component was clicked.
If for some reason you want to avoid this, also consider the fact the bind itself will cause a lot of overhead. You are essentially creating a new function with every component.
I'm faced with code today that approached this "bubbling onClick on parent, data attributes on children" strategy everywhere. It's so painfull to read.
I'd suggest using a virtual rendering system like react-table to only render the few categories visible to a user. You get a real performance boost, because you simply don't render 90% of your 20000 divs to start with. And it solves the problem of event listener with bound functions eating memory.

Handling react events

We're creating a new site and we chose reactjs to do this. And right now I'm researching on events in Javascript and luckily I stumbled on this site where he teaches how to handle events for many elements and letting the parent handle the event and basically is what we are doing for out site as well right now.
As I continued to research, I also stumbled upon this answer here (more react oriented) where it says I would pass the function/method from the parent to the child.
Is it promoting the same way as what was mentioned on the first link? Also, is this the way to do this (like the first link to handle events) in reactjs? Should I implement somewhere along this lines?
Follow up question... or should I ask this separately?
If I have
<Parent>
<ChildComponent>
<ChildComponent>
...
and a child when clicked I add a class to it, say .selected. But I'd need to remove it to the others since they are not selected anymore. In jQuery I could've done something like
$('.child-components').removeClass('selected');
$(this).addClass('selected');
How to do this the react way?
For your first question, it would depend on the exact situation and the relationship between the parent and child component.
For example, handling a click event for one child component (out of many similar others under the same parent) would be better done by the parent.
But as a counterexample, consider a component that's a text input. It might perform its own validation before passing it to the parent (which could be the form that handles submission), in which case handling the change events would be better done within the component itself rather than by the parent.
In general, yes, the React way to handle events is to pass the event handler functions from parent to child as necessary.
For your second question, you just need to store the index (or some other ID) of the selected child component in the parent's state. Then, in the parent's render method (where I assume the child components are also rendered), use that information to add the .selected class to the proper child component. So the parent knows which child is selected, and takes care of rendering them properly.
The other way around, you can then update the parent's state based on child events (e.g., setting selection when a child is clicked) by using a click handler function passed from the parent to the child via props.
Hope this helps!
The article you mentioned is using event bubbling. It attaches one event handler, and lets all events bubble up to it. It's a very good approach, and it's actually what React does internally. It only seems like you attach event handlers directly to an element when you do <div onClick={this.handleClick} /> but what React does is to only set up one click listener for the top node. It only calls your this.handleClick if that event bubbled up to that element.
For your second question, you would do something like this:
var Child = React.createClass({
render: function () {
return <li className={this.props.className} onClick={this.props.onClick}>{this.props.text}</li>;
}
});
var Parent = React.createClass({
getInitialState: function () {
var items = [
{text: 'One'},
{text: 'Two'},
{text: 'Three'}
];
var selected = items[0];
return {
items: items,
selected: selected
};
},
handleClick: function (item) {
this.setState({selected: item});
},
render: function () {
var self = this;
return (
<ul>
{this.state.items.map(function (item) {
return (
<Child
key={item.text}
className={this.state.selected === item ? 'selected' : ''}
onClick={self.handleClick.bind(self, item)}
text={item.text}
/>
);
})}
</ul>
);
}
});
Here's a working JSBin: http://jsbin.com/libunecaba/1/edit?js,output

Categories

Resources