onChange event is not working on my Select - javascript

Hi i want to do something when mi select box changes, i have something like this:
function Task(){
const prints = (e) =>{
console.log("it prints");
}
const prints2 = (e) =>{
console.log("it prints2");
}
return (
<select onChange={prints} onClick={prints2} name="subject">
{subjectsContext.subjects.map((aSubject)=>(
<option value={aSubject.idsubject}>
{aSubject.subjectname}
</option>
)
)}
</select>
)
}
subjects is a list that i have, when i put onClick instead of onChange it works fine, but when i put onChange it dont works when i select a option, it just dont trigger the prints function, why?

You need to pass the event (e) to the function as a parameter, try this:
function Task() {
const prints = (e) => {
console.log(e.target.value);
console.log('it prints');
};
const prints2 = (e) => {
console.log(e.target.value);
console.log('it prints2');
};
return (
<select
onChange={(e) => prints(e)}
onClick={(e) => prints2(e)}
name='subject'
>
{subjectsContext.subjects.map((aSubject) => (
<option value={aSubject.idsubject}>{aSubject.subjectname}</option>
))}
</select>
);
}
Also - consider adding a key attribute to you option as you're mapping trough it and creating duplicate elements in the DOM, I assume this is a react app so it might cause errors.
Edit: ... And you don't need the onClick here, you won't even get an event so simply remove it.

Related

How to make onChange only fire once value is changed in React

I'm using a range input in my React application. I would like to log the value selected once the range slider has changed (not while it is changing). Here is an example of my issue:
const App = () => {
const handleChange = (e) => {
console.log(e.target.valueAsNumber);
}
return <input type="range" onChange={handleChange}/>
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
If you view the browser console, the value is being logged as you slide the range slider. My desired output is so that the handleChange callback is only fired once you release the slider. I would like it to behave in a similar way that onChange behaves in vanilla HTML:
const handleChange = (e) => {
console.log(e.target.valueAsNumber);
}
<input type="range" onChange="handleChange(event)"/>
It seems like react has made onChange behave like onInput, which in my case is undesirable. Is there a simple prop that I'm missing to make this work? Or do I need to use refs or some other method to make this work as intentded?
The behaviour you want is of onchange event listener. Unfortunately, React connects onChange prop to oninput event handler.
There's also an active issue about this: https://github.com/facebook/react/issues/3964 Document how React's onChange relates to onInput #3964
Simple fix would be to use onchange event listener using ref.
const NewInput = (props) => {
const setRef = useCallback((e) => {
e.target.onchange = props.onChange || null;
}, [props.onChange]);
return <input ref={setRef} {...props} />
}
Another approach is discussed here: In React, what's the difference between onChange and onInput?

Drag and Drop API with React not dropping or returning id

I'm following this tutorial and I'm having trouble with the onDragStart and onDragOver not returning and information. More context, I'm making a sticky note type app.
onDragStart = (e, id) => {
e.dataTransfer.setData("id", id);
};
onDragOver = (e) => {
e.preventDefault();
};
onDrop = (e, sect) => {
let id = e.dataTransfer.setData("id", e.target.id);
let notes = this.state.notes.filter((note) => {
console.log(id);
if (note.key == id) {
note.section = sect;
}
return note;
});
this.setState({
...this.state,
notes,
});
};
This is the code I used that is not working properly. I can drag the notes but they don't drop. I believe this is because variable id is undefined for some reason. For example, if I removed the (note.key == id) the notes would drop correctly into place (but every note would and not the specific one I want).
As for the Sticky notes and droppable area, here is the code.
this.state.notes.forEach((n) => {
sections[n.section].push(
<Sticky
key={n.key}
onDragStart={(e) => this.onDragStart(e, n.key)}
id={n.key}
text={n.text}
/>
);
});
return (
<div className="App">
<div className="container-drag">
<h2 className="header">General</h2>
<div
id="general"
className="droppable"
onDragOver={(e) => this.onDragOver(e)}
onDrop={(e) => {
this.onDrop(e, "general");
}}
>
{sections.general}
</div>
</div>
<div className="container-drag">
<h2 className="header">Todo</h2>
<div
id="todo"
className="droppable"
onDragOver={(e) => this.onDragOver(e)}
onDrop={(e) => {
this.onDrop(e, "todo");
}}
>
{sections.todo}
</div>
</div>
</div>
);
}
And the sticky component is simply
<div id={this.props.id} className="draggable" draggable>
<p>{this.props.text}</p>
</div>
I don't think the problem is with the state so I'll leave that out, it is just an array of objects. In addition, I've tried using text instead of id but I was a bit confused so maybe I did something incorrectly. Basically, I've tried console.logging everything individually with no success. I'm not sure the problem but I found that copy-pasting the tutorials code made it work perfectly (but I still wanted my own take of it, with separate components).
Hopefully I can find a solution and I appreciate any help or tips.
I solved the problem.
onDragStart = (e, id) => {
e.dataTransfer.setData("id", id); //in brackets should be ("text/plain", id) and in the .getData below it should be (text", id)
};
onDragOver = (e) => {
e.preventDefault();
};
onDrop = (e, sect) => {
let id = e.dataTransfer.setData("id", e.target.id); //this shoud be .getData not .setData
And finally,
<Sticky
key={n.key}
onDragStart={(e) => this.onDragStart(e, n.key)}
id={n.key}
text={n.text}
/>
The onDragStart was being passed as a property not an actual function, I solved this by changing it to a

I need to set a function as eventlistener for an html element

I have this html code
<div class = "select">
<select class = "select-text">
<option disabled selected> Select user </option>
</select>
</div>
<button id = "oracle" class = "mdc-button"> Calculate BMI </button>
And this js code
const displaySelectedUser = () => {};
const letsCalculateBMI = () => {};
const powerupTheUI = () => {};
Using only DOM selector in the powerupTheUI function I am to
set displaySelectedUser as a change eventlistener for the div select class in the html
set letsCalculateBMI as a click event listener for the #oracle button
I've tried
document.querySelector(".select").addEventListener("change", displaySelectedUser());
document.querySelector("#oracle").addEventListener("click", letsCalculateBMI());
And
let oracle = document.querySelector("#oracle");
oracle.addEventListener("click", () => {
letsCalculateBMI();
});
But the test program won't accept it
You are invoking the function which be return the value return from function. Just don't call the function.
document.querySelector(".select").addEventListener("change", displaySelectedUser);
document.querySelector("#oracle").addEventListener("click", letsCalculateBMI);
Note: You will not be able to access the element clicked/changed by using this in a event handler if you are using Arrow Functions

Inner button onClick event not being fired with React

Hi I have something like this in a React application:
<li onClick={this.props.onToggleTodo}>
Todo
<button onClick={this.props.onRemove}>Remove</button>
<li>
But when I click on remove button... the handler onToggleTodo is fired but onRemove isn't.
I fixed by doing this on handler:
onToggleTodo = t => (e) => {
if(e.target.tagName == 'LI'){
this.setState({done: true});
}
}
It wasn't firing because of setState because it makes React render again...
That is NOT the best way, RIGHT?????
Here is the demo and the code
It's probably because the onToggleTodo is on the li and the li is a container for the button... You might want to try working as following:
<li>
<span onClick={this.props.onToggleTodo}>Todo</span>
<button onClick={this.props.onRemove}>Remove</button>
<li>
edit
If you REALLY want to work the way you were doing at first, I think this should work also:
<li {todoToggled ? onClick={this.props.onToggleTodo}}>
Todo
<button onClick={this.props.onRemove}>Remove</button>
<li>
onToggleTodo = () => {
this.setState({
todoToggled = true
})
//Rest of your code
}
onRemove = () => {
this.setState({
todoToggled = false
})
//Rest of your code
}
I'm not 100% if the syntax is correct here because I'm writing this without any feedback, but I think it's headed in the correct direction.
its javascript.. so..
onToggleTodo = t => (e) => {
this.setState({done: true});
}
onRemove = t => (e) => {
e.stopPropagation();
console.log(e.target);
}

React controlled select element reverts to first option

I am using React to create a controlled select element. My app validates the new option value selected by the user and then updates that value on the element.
The validation takes some time and the behavior I observe is as follows:
User selects an option
Element changes the selected option to index 0
Once the validation period is over, the element updates itself to the new value
I do not understand why the element reverts to the first index and how can I prevent this (except by setting the value immediately which is undesired in my application)
Use this JSFiddle to see this behavior (select Option2) https://jsfiddle.net/qo39g2j4/
var Select = React.createClass({
render: function() {
return (
<select id="my-select" value={this.props.value} onChange={this.props.onChange}>
<option value="Option1">Option1</option>
<option value="Option2">Option2</option>
<option value="Option3">Option3</option>
</select>
);
}
});
var Wrapper = React.createClass({
getInitialState: function () {
return {value: "Option3"};
},
onChange: function (event) {
var value = event.target.value;
setTimeout(function () {
this.setState({value: value});
}.bind(this), 1000); // this simulates the validation period
},
render: function () {
return (
<Select
value={this.state.value}
onChange={this.onChange}/>
);
}
});
ReactDOM.render(
<Wrapper/>,
document.getElementById("container")
);
Try using defaultValue instead of value in your Select component. Or add event.preventDefault() in onChange.
I had the same problem when integrating redux-form within my app, due to an issue in onBlur event handler,
so here is how i handled it using customSelect react component :
export default class CustomSelect extends Component {
render() {
const { children, value, onBlur, ...props } = this.props
return (
<select
{...props}
onBlur={() => onBlur(value)}
>
{children}
</select>
)
}
}
Here onBlur eventHandler is explicitly called with the value passed, to keep it selected
Also validation on client-side or server-side should not be represented by a timeout as user is expected to see the wrong input data with the validation message if invalid,
I guess your implementation means that selected option would be visible only if valid which is not the best user experience you can have, if that's the actual case
Solution is based on redux-form react-select bug

Categories

Resources