React onChange handler is being called multiple times during page load - html

We're trying to add a onChange handler to one of our custom components - namely, a Checkbox component (the only reason for it being a custom component is so that we can efficiently encapsulate the intermediate HTML attribute). It looks something like this:
<Checkbox
id="select-all"
onChange={this.handleSelectAllChange(ids)}
indeterminate={isIndeterminate}
checked={areVisibleItemsSelected}
disabled={isDisabled}
/>
The handler function is structured somewhat like this:
handleSelectAllChange(ids) {
// omitted code that filters on ids and produces newIds
this.props.updateIds(newIds);
}
Where this.props.updateIds is a passed-down function that modifies the parent component's state.
The problem is that this function is called about 10 times during page load, which is not intended. I thought it was only called when the actual checkbox element is modified?

By declaring it like this onChange={this.handleSelectAllChange(ids)} the method call happens immediately at rendering the CheckBox. With ES6 you can avoid this by using
onChange={() => this.handleSelectAllChange(ids)}
This means you pass a new function which will call handleSelectAllChange on change.

I just had the same issue... I was able to fix the problem stopping the propagation of the event.
Add this in the function being called by your onChange event:
e.stopPropagation();

Pass the handler function like
<Checkbox
id="select-all"
onChange={this.handleSelectAllChange.bind(this,ids)}
indeterminate={isIndeterminate}
checked={areVisibleItemsSelected}
disabled={isDisabled}
/>

You should define onClick inside the element itself, and pass a pointer to the handler function:
function Checkbox(props) {
return (<input type="checkbox" value={props.value} key={props.value}
onClick={props.clickHandler} />); // actual onclick
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
boxes: ['1', '2', '3'],
}
}
handleSelectAllChange(box) {
console.log(box)
}
render() {
const boxes = this.state.boxes.map((b,i) =>
<Checkbox value={b}
clickHandler={this.handleSelectAllChange.bind(this, b)} // pass click handler
key={i}/>
);
return (
<div>
{boxes}
</div>
);
}
}
ReactDOM.render(<App/>,
document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app">Loading...</div>

this.handleSelectAllChange(ids) means call the function.
You should pass a function object to event handlers. e.g.()=>{this.handleSelectAllChange(ids)}

Related

Event listener for button does not work unless page is refreshed in React

In my index.html, I have the following code:
<head>
...
<script type="text/javascript"
src="https://mysrc.com/something.js&collectorId=f8n0soi9"
</script>
<script type="text/javascript">window.ATL_JQ_PAGE_PROPS = {
'f8n0soi9': {
"triggerFunction": function (showCollectorDialog) {
document.getElementById("button1").addEventListener("click", function (e) {
e.preventDefault();
showCollectorDialog();
});
}
}
};</script>
</head>
and then in my myComponent.tsx file, I have a button somewhere on the page that looks like this:
function myComponent() {
return (
...
<button id="button1">
Button Text
</button>
...
);
}
export default myComponent;
It's probably also important to note that I'm using react-routing to navigate between various components, and the button above is just in one of those components
So the issue seems to be that if you load in to a site on any other webpage and later navigate to the page with the button on it, the button won't work unless you refresh that specific page, since presumably it wasn't on the first page loaded and perhaps no element with id "button1" was found to bind the event listener to. React-routing doesn't refresh the page by default when navigating through the site.
Putting the code I have in the index.html file into the myComponent.tsx file also does not work, since (I think) the index.html file allows for any raw html but the tsx file isn't truly html? Is there perhaps a way to define this as a function in the index.html file and then assign an onClick event to the button? Thank you all in advance!
Yes, it should be possible to bind the function that shows the dialog for later use and then use the recommended React event on the button.
In this example bound globally to the window object for simplicity:
"triggerFunction": function (showCollectorDialog) {
window.showCollectorDialog = showCollectorDialog;
}
// ...
<button id="button1" onClick={e => {e.preventDefault(); window.showCollectorDialog();}}>
If you run into Type errors with that, try (on the button):
=> {...; (window as any).showCollectorDialog();}
Or declare only the property (possible be more specific than any here if the signature is known):
declare global {
interface Window { showCollectorDialog: any; }
}
It should be fine to have this just somewhere in your TS source, your index.html without TS should just assign it.

How to dynamically append HTML element to component in Vue.js

I'm new to vue.js, before this i'm using jquery or js for my project, i'm working on a project that require me to append HTML element dynamically on button click, and at the same time bind the input value to model, similar to:
$(".button").click(function() {
$("#target").append("<input type='hidden' name='data' v-model='inputModel' value='1'/>");
});
But i need this in Vue.js ways.
Here is my code:
data() {
return {
programmeBanner: [],
dropzoneOptions: {
...
...
init: function () {
this.on("success", function(file, response) {
file.previewElement.id = response;
// this is the part that i want to append the html input into
// the .dz-preview is the target that i want to append
$(".dz-preview[id='"+response+"']").append("<input type='hidden' name='"+fileInputName+"[]' v-model='programmeBanner' class='"+fileInputName+"' value='"+response+"'/>");
});
},
...
Here is a sample that i want to achieve, this is in Jquery, i need it in Vue.js
https://jsfiddle.net/041xnfzu/
Hmm I think you're mixing all kinds of code here :)
First off, you shouldn't use jquery inside VueJS. I think that your setup is a little off. You shouldn't define a whole object with functions and event listeners in your vue data object.
That's what Vue components are for, define methods in your methods property and data in you data property.
Thanks to your jsfiddle example, I have this pure vuejs example for you on codepen: https://codepen.io/bergur/pen/vwRJVx
VueJS code:
new Vue({
el: '#demo',
name: 'Adding html',
data() {
return {
inputs: []
}
},
methods: {
addInput() {
this.inputs.push(this.inputs.length+1)
}
},
computed: {
buttonText() {
return this.showInput ? 'Hide input' : 'Show input'
}
}
})
HTML template
<div id="demo">
<button #click="addInput">Add input</button>
<div v-for="(input, index) in inputs">
<input name="data" v-model="inputs[index]" />
</div>
<p>
First value: {{ inputs[0] }}<br />
Second value: {{ inputs[1] }}
</p>
</div>
Here's a walkthrough of the code.
We create a data property called inputs, that is an array.
We create a method called addInput and all that does is to push a new item into the inputs array
In the template we loop with v-for through our inputs array and render a input for each item in our inputs data property.
We then use v-model to bind each input to its corresponding place in the inputs array.
You can try to change the input value and see that the template updates the value.
So input[0] holds the value for the first input, input[1] holds the value for the second input and so on.
If you want only one element to be appended to component, then you should use v-if
https://v2.vuejs.org/v2/guide/conditional.html#v-if
If you want to append multiple elements, like todo list, you should use v-for
https://v2.vuejs.org/v2/guide/#Conditionals-and-Loops

React OnClick Function not Binding

I have an onClick handler that is bound to currentAreas component. The onClick handler is called simultaneously when the currentAreas component is called, but does not work after that.
onClick does not work when I try to click the anchor tag and I think it might be due to the way I'm binding the onClick function.
currentAreas.js
import React, { Component, PropTypes } from 'react';
export default class currentAreas extends Component {
constructor(props) {
super(props);
}
render() {
const { onClick } = this.props;
return(
<div className="list-group panel">
<a href="#demo3" className="list-group-item" onClick={onClick("All", "All")} >All</a>
</div>
);
}
}
currentList.js (Main component)
class currentList extends Component {
constructor(props) {
super(props);
this.updateLocation = this.updateLocation.bind(this);
}
updateLocation(name, nameLocation){
console.log(name);
console.log(nameLocation);
}
render() {
return (
<div className="step-3-container">
<CurrentAreas
onClick ={this.updateLocation} />
</div>
)
}
}
Whenever your javascript interpreter "sees" a "()" it tries to execute the function that is before "()". Therefore, what your code is actually doing is executing onClick (the one that came from props), passing as arguments "All" and "All". So, what you need to do is, instead of calling it yourself, let the onClick handler call it.
In other words, the onClick prop must receive a function.
One possible solution is to wrap your onClick into an arrow function, doing something like that:
<a href="#demo3" onClick={() => onClick("All", "All")}>All</a>
Or, you may bind the parameters to your function (bind returns a function, so it will fit well on onClick).
<a href="#demo3" onClick={onClick.bind(null, "All", "All")}>All</a>
The first bind parameter is the this value inside the binded function.
FMI about bind: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
Youre executing the function during the declaration of the component, which likely means youre triggering it on render, not click
onClick={onClick("All", "All")}
needs to be
onClick={onClick.bind(null, "All", "All")}
or
onClick={function(){ onClick("All", "All"); }}
The onClick of a react component needs a callback that can be executed. you are passing in the return value of this.props.onClick because youre calling it right away with arguments

How can I access a hover state in reactjs?

I have a sidenav with a bunch of basketball teams. So I would like to display something different for each team when one of them is being hovered over. Also, I am using Reactjs so if I could have a variable that I could pass to another component that would be awesome.
React components expose all the standard Javascript mouse events in their top-level interface. Of course, you can still use :hover in your CSS, and that may be adequate for some of your needs, but for the more advanced behaviors triggered by a hover you'll need to use the Javascript. So to manage hover interactions, you'll want to use onMouseEnter and onMouseLeave. You then attach them to handlers in your component like so:
<ReactComponent
onMouseEnter={() => this.someHandler}
onMouseLeave={() => this.someOtherHandler}
/>
You'll then use some combination of state/props to pass changed state or properties down to your child React components.
ReactJs defines the following synthetic events for mouse events:
onClick onContextMenu onDoubleClick onDrag onDragEnd onDragEnter onDragExit
onDragLeave onDragOver onDragStart onDrop onMouseDown onMouseEnter onMouseLeave
onMouseMove onMouseOut onMouseOver onMouseUp
As you can see there is no hover event, because browsers do not define a hover event natively.
You will want to add handlers for onMouseEnter and onMouseLeave for hover behavior.
ReactJS Docs - Events
For having hover effect you can simply try this code
import React from "react";
import "./styles.css";
export default function App() {
function MouseOver(event) {
event.target.style.background = 'red';
}
function MouseOut(event){
event.target.style.background="";
}
return (
<div className="App">
<button onMouseOver={MouseOver} onMouseOut={MouseOut}>Hover over me!</button>
</div>
);
}
Or if you want to handle this situation using useState() hook then you can try this piece of code
import React from "react";
import "./styles.css";
export default function App() {
let [over,setOver]=React.useState(false);
let buttonstyle={
backgroundColor:''
}
if(over){
buttonstyle.backgroundColor="green";
}
else{
buttonstyle.backgroundColor='';
}
return (
<div className="App">
<button style={buttonstyle}
onMouseOver={()=>setOver(true)}
onMouseOut={()=>setOver(false)}
>Hover over me!</button>
</div>
);
}
Both of the above code will work for hover effect but first procedure is easier to write and understand
I know the accepted answer is great but for anyone who is looking for a hover like feel you can use setTimeout on mouseover and save the handle in a map (of let's say list ids to setTimeout Handle). On mouseover clear the handle from setTimeout and delete it from the map
onMouseOver={() => this.onMouseOver(someId)}
onMouseOut={() => this.onMouseOut(someId)
And implement the map as follows:
onMouseOver(listId: string) {
this.setState({
... // whatever
});
const handle = setTimeout(() => {
scrollPreviewToComponentId(listId);
}, 1000); // Replace 1000ms with any time you feel is good enough for your hover action
this.hoverHandleMap[listId] = handle;
}
onMouseOut(listId: string) {
this.setState({
... // whatever
});
const handle = this.hoverHandleMap[listId];
clearTimeout(handle);
delete this.hoverHandleMap[listId];
}
And the map is like so,
hoverHandleMap: { [listId: string]: NodeJS.Timeout } = {};
I prefer onMouseOver and onMouseOut because it also applies to all the children in the HTMLElement. If this is not required you may use onMouseEnter and onMouseLeave respectively.
This won't work for OP because they wanted a variable but for those who just want a UI hover effect it's usually easier to stick with CSS.
Below example will reveal a delete button when an item is hovered over:
<div className="revealer">
<div>
{itemName}
</div>
<div className="hidden">
<Btn label="Delete"/>
</div>
</div>
.hidden {
display: none;
}
.revealer:hover .hidden {
display: block;
}
Parent div has revealer class. When it's hovered over, it'll reveal the hidden div. Hidden div must be nested inside revealer div.
You can implement your own component logics using those events which stolli and BentOnCoding suggested above, or use the module named react-hover
if I could have a variable that I could pass to another component that would be awesome.
then you can simply wrap another component
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<TriggerComponent />
</Trigger>
<Hover type="hover">
<HoverComponent />
</Hover>
</ReactHover>
or your plain HTML:
<ReactHover options={optionsCursorTrueWithMargin}>
<Trigger type="trigger">
<h1 style={{ background: '#abbcf1', width: '200px' }}> Hover on me </h1>
</Trigger>
<Hover type="hover">
<h1> I am hover HTML </h1>
</Hover>
</ReactHover>
demo code here: demo
Using useState,
import React, { useState } from "react";
function App() {
const [ishover,sethover]=useState(false);
function MouseOver() {
sethover(true);
}
function MouseOut() {
sethover(false);
}
return (
<div>
<button
style={{backgroundColor: ishover?"black":null}}
onMouseOver={MouseOver}
onMouseOut={MouseOut}
onClick={handleClick}
>
Submit
</button>
</div>
);
}
export default App;
You can try to implement below code. Hover functionality can be acheived with Tooltip.
Please refer below code and link for clarity
https://mui.com/material-ui/react-tooltip/
import * as React from 'react';
import DeleteIcon from '#mui/icons-material/Delete';
import IconButton from '#mui/material/IconButton';
import Tooltip from '#mui/material/Tooltip';
export default function BasicTooltip() {
return (
<Tooltip title="Delete">
<IconButton>
<DeleteIcon />
</IconButton>
</Tooltip>
);
}

How can I detect keydown or keypress event in angular.js?

I'm trying to get the value of a mobile number textbox to validate its input value using angular.js. I'm a newbie in using angular.js and not so sure how to implement those events and put some javascript to validate or manipulate the form inputs on my html code.
This is my HTML:
<div>
<label for="mobile_number">Mobile Number</label>
<input type="text" id="mobile_number" placeholder="+639178983214" required
ngcontroller="RegisterDataController" ng-keydown="keydown">
</div>
And my controller:
function RegisterDataController($scope, $element) {
console.log('register data controller');
console.log($element);
$scope.keydown = function(keyEvent) {
console.log('keydown -'+keyEvent);
};
}
I'm not sure how to use the keydown event in angular.js, I also searched how to properly use it. And can i validate my inputs on the directives? Or should I use a controller like what I've done to use the events like keydown or keypress?
Update:
ngKeypress, ngKeydown and ngKeyup are now part of AngularJS.
<!-- you can, for example, specify an expression to evaluate -->
<input ng-keypress="count = count + 1" ng-init="count=0">
<!-- or call a controller/directive method and pass $event as parameter.
With access to $event you can now do stuff like
finding which key was pressed -->
<input ng-keypress="changed($event)">
Read more here:
https://docs.angularjs.org/api/ng/directive/ngKeypress
https://docs.angularjs.org/api/ng/directive/ngKeydown
https://docs.angularjs.org/api/ng/directive/ngKeyup
Earlier solutions:
Solution 1: Use ng-change with ng-model
<input type="text" placeholder="+639178983214" ng-model="mobileNumber"
ng-controller="RegisterDataController" ng-change="keydown()">
JS:
function RegisterDataController($scope) {
$scope.keydown = function() {
/* validate $scope.mobileNumber here*/
};
}
Solution 2. Use $watch
<input type="text" placeholder="+639178983214" ng-model="mobileNumber"
ng-controller="RegisterDataController">
JS:
$scope.$watch("mobileNumber", function(newValue, oldValue) {
/* change noticed */
});
You were on the right track with your "ng-keydown" attribute on the input, but you missed a simple step. Just because you put the ng-keydown attribute there, doesn't mean angular knows what to do with it. That's where "directives" come into play. You used the attribute correctly, but you now need to write a directive that will tell angular what to do when it sees that attribute on an html element.
The following is an example of how you would do that. We'll rename the directive from ng-keydown to on-keydown (to avoid breaking the "best practice" found here):
var mod = angular.module('mydirectives');
mod.directive('onKeydown', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
// this next line will convert the string
// function name into an actual function
var functionToCall = scope.$eval(attrs.ngKeydown);
elem.on('keydown', function(e){
// on the keydown event, call my function
// and pass it the keycode of the key
// that was pressed
// ex: if ENTER was pressed, e.which == 13
functionToCall(e.which);
});
}
};
});
The directive simple tells angular that when it sees an HTML attribute called "ng-keydown", it should listen to the element that has that attribute and call whatever function is passed to it. In the html you would have the following:
<input type="text" on-keydown="onKeydown">
And then in your controller (just like you already had), you would add a function to your controller's scope that is called "onKeydown", like so:
$scope.onKeydown = function(keycode){
// do something with the keycode
}
Hopefully that helps either you or someone else who wants to know
You can checkout Angular UI # http://angular-ui.github.io/ui-utils/ which provide details event handle callback function for detecting keydown,keyup,keypress
(also Enter key, backspace key, alter key ,control key)
<textarea ui-keydown="{27:'keydownCallback($event)'}"></textarea>
<textarea ui-keypress="{13:'keypressCallback($event)'}"></textarea>
<textarea ui-keydown="{'enter alt-space':'keypressCallback($event)'}"> </textarea>
<textarea ui-keyup="{'enter':'keypressCallback($event)'}"> </textarea>
JavaScript code using ng-controller:
$scope.checkkey = function (event) {
alert(event.keyCode); //this will show the ASCII value of the key pressed
}
In HTML:
<input type="text" ng-keypress="checkkey($event)" />
You can now place your checks and other conditions using the keyCode method.