React controlled select element reverts to first option - javascript

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

Related

onChange event is not working on my Select

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.

(React.js + Hooks) How to reset input field after button click?

What I want to do is have an input that a user can type into. Upon clicking a button, take that input and process it and perform some backend operations with the database connected to the input. I'm trying to accomplish this with a state hook: onBlur, save input into state. On button click, do two things: 1) take the state variable and pass it to the backend resolvers, and 2) clear the input field so it's empty, and only the placeholder text exists.
I have this code:
const [inputVal, setInputVal] = useState("");
updateInput = (e) => {
const val = e.target.value;
setInputVal(val);
}
sendData = async () => {
//handle async backend processing with inputVal
setInputVal("");
//rerender
}
<Button className="input-button" onClick={sendData}>Send Data</Button>
<input className="input-field" placeHolder="Input name." defaultValue={inputVal} onBlur=(updateInput)></input>
However, I have two issues.
On button click, I want to first updateInput, and then handle the button click, so it always uses the new value. However, it seems as if there are some issues with that, possibly due to the asynchronous nature of sendData?
While inputVal may be an empty String, for some reason, the value in the input box doesn't reset to nothing, it stays exactly as it was (although the internal data would still have inputVal = 0). And onBlur it reupdates the inputVal.
for a controlled state input the most common approach is to use onChange rather than onBlur. This would also avoid your conflicts with blur and click events. Also you would pass inputVal to value input's property.
const [inputVal, setInputVal] = useState("");
const updateInput = (e) => {
const val = e.target.value;
setInputVal(val);
}
const sendData = async () => {
//handle async backend processing with inputVal
setInputVal("");
//rerender
}
return (
<>
<button className="input-button" onClick={sendData}>Send Data</button>
<input className="input-field" onChange={updateInput} placeHolder="Input name." value={inputVal}/>
</>
);
First, change defaultValue to value={inputVal} since it's a controlled input.
Secondly, please elaborate on what issues you have in 1.

I need to fire a "change" event at the init of an Alpine.js component

I'm new to Alpine.js and I'm struggling with this simple task.
I have two select in a form. I populate the first with a JS script then when the user select a value the script populate the second select.
My problem is that when an user come back on the same form after a submit
I need to fire a change event on the first select to have the second ready for the value I have to select.
Here my code.
I get this.$refs.select_one.change is not a function
Thank you for your help
<div x-data="load_form()" x-init="init">
<form>
<select name="first" x-ref="select_one" x-model="first_select"></select>
<select name="second" x-model="second_select"></select>
</form>
</div>
function load_form() {
return {
first_select: "",
second_select: "",
init() {
script_that_populate_selects("#select1", "#select2");
// set the first
this.first_select = "value1";
// this should fire the change event
this.$nextTick(() => this.$refs.select_one.change());
// wait before set the second value
setTimeout(()=> {
this.second_select = "value2";
}, 1500);
}
}
}
I understood my fault
This is the right instruction to fire a change event
this.$nextTick(() => this.$refs.select_one.dispatchEvent(new Event("change"))

Initializing then updating textarea value in redux app.

Inside my render function I have
<textarea value={ this.props.song.client_lyrics }
onKeyUp={ (event) => this.props.editLyrics(event.target.value) } >
</textarea>
Normally the editLyrics function will dispatch an action which updates the client_lyrics. But since I have value = { this.props.song.client_lyrics } instead of event.target.value sending the initial value + what I typed, it only keeps sending over the original client_lyrics which just keeps setting client_lyrics to itself.
How can I initialize the textarea with a value and then append more characters to it so the client_lyrics prop actually changes?
Make use of an onChange instead of an onKeyUp event on the textArea
<textarea value={ this.props.song.client_lyrics }
onChange={ (event) => this.props.editLyrics(event.target.value) } >
</textarea>

Add class on focus change using Redux and React

I'm trying to add a class to a div wrapping an input on focus with Redux and React.
My React component looks like this:
import React from 'react'
const Input = ({ value, onChange }) => (
<div className="">
<input type="email" onChange={onChange} value={value} />
</div>
)
export default Input
When a user focuses on the input, I want the class selected to be added to the div. The component is connected using react-redux.
The two ways I've tried so far:
Attempt 1
Add an onFocus listener to the input and dispatch an action that adds the inputs focus state into the inputs state , for example:
{
value: '',
focused: true
}
If focused is true, add the class with className={focused ? 'selected' : ''}
My concern with this approach is I could get into issues where multiple inputs have focused: true. So I'd have to manage that myself and look through the whole input state and reset to false before setting focus on a new input.
Atempt 2
Add an onFocus listener to the input and dispatch an action that contains the ID of the focused input and store the ID in the state. This would solve the issue of having to reset all other inputs to false.
My concern with this is I'd have to give every input I wanted this feature on a unique ID (this could possibly use Reacts ID). I'd have to add something like className={ID === focusedID ? 'selected' : '')
I feel like there must be a better way to achieve this. Any help would be appreciated.
Thanks
You dont even need Redux for that, just store the "selected" state in the component.
Something like this
var InputComp = React.createClass({
getInitialState: function() {
return {focus: false}
},
onFocus : function(){
this.setState({focus: true})
},
onBlur: function(){
this.setState({focus: false})
},
getClass: function(){
if(this.state.focus === true)
return "selected";
else
return "";
},
render: function() {
var inputClass = this.getClass();
return <div className={inputClass}><input onBlur={this.onBlur} onFocus={this.onFocus}/></div>
}
});
ReactDOM.render(
<InputComp/>,
document.getElementById('container')
);
jsfiddle
You need to use the :focus pseudo-selector. You need to add a class to your divs, for instance, focusable. If that is done, you need this CSS rule:
.focusable:focus {
/* Your rules for selected */
}

Categories

Resources