React - setting input value with JavaScript does not trigger 'onChange' - javascript

In my React app (version 15.4.2), I am updating the value of a text input field with JavaScript - however, I have an onChange event listener associated with the input field, and changing the value of the input field does not trigger the handler (good old fashioned typing in the input field does, of course), though the content of the field is correctly updated.
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event){
let attribute = event.target.name;
let updatedGroup = this.state.group;
updatedGroup[attribute] = event.target.value;
this.setState({group: updatedGroup});
}
addMember(memberId) {
let inputField = document.getElementById("members");
let inputValues = inputField.value.split(",");
inputField.value = [...inputValues, memberId];
}
render(){
<input type="text" id="members" name="members" value={this.state.group.members} onChange={this.onChange} />
}
So when addMember() is called (via button click in a child component), then content of the input field itself is correctly updated, but onChange is not called and thus the state is not updated, etc...
Is there a way that I can programatically set the value of the input field and trigger onChange?

What I always do in this situation is have your onChange event handler be a function that passes the event data (the character that was entered or the aggregate string) into another function. I put all of the business logic in that function. That way, if I want to invoke the business logic, I just call that method.
Since you are asking "Is there a way that I can programatically set the value of the input field and trigger onChange?" Why not skip onChange and call the business logic function from your function that is programmatically setting the value?

Onchange method will get triggered only when you type something, if you use document.getElementById and replace its values, it will directly replace the value in DOM, onChange will not get triggered in that case. Since you are using the react, I think you should avoid the direct DOM manipulation.
You are using controlled input, so in addMember method instead of updating the value in DOM, update the state value.
Try this addmember method:
addMember(memberId) {
//let inputField = document.getElementById("members");
//let inputValues = inputField.value.split(",");
//inputField.value = [...inputValues, memberId];
let group = this.state.group.slice();
group[members] = group[members] + ',' + memberId;
this.setState({
group
});
}

Related

React Hooks - losing focus after typing one value

My form component inputs loses focus everytime I type and change the state, dunno why it loses focus.
This is a link to the sandbox with the same issue
https://codesandbox.io/s/quiet-dew-cf5wr?file=/src/App.js
Have you try to put const MotionStack = motion(Stack); outside of the Form function?
...
+ const MotionStack = motion(Stack);
function Form() {
- const MotionStack = motion(Stack);
...
}
I think that is messing with the lifecycle, because is getting re-generated every single time you update the state.
Another option is to use React.useMemo, to prevent MotionStack from being re-generated.
const MotionStack = React.useMemo(() => motion(Stack), []);
This already has been solved, my Motion div was inside the component resulting it on also rerendering everytime I type. I just moved it out the function.

form.io how to trigger custom validation on blur?

In form.io form builder i am adding custom validation but the default is to trigger the validation on change i want to set it to on blur.
I have tried the following code on the custom validation tab:
let field = document.querySelector('input[name="data[nametest]"]');
field.addEventListener("blur", checkValidation);
function checkValidation() {
console.log('checking...');
valid = (input.length > 5) ? true : 'Test name must be at least 5 characters long' ;
}
the code is running on blur but it is not showing the error the valid global variable is set to the correct error message its just not showing on the form also i notice that the more characters on the textfield the more the event gets trigger on blur, I would be gratefull for any help.
Thanks!
When you are in the Form Builder view, validity checks are disabled. You may test simple validations in the component settings modal:
Now, in case you want to trigger an action on blur, you must consider that custom validation is not persistent and is calculated on every evaluation, meaning that whatever you do with it after it has been evaluated will not affect the component itself. You will need to attach the event directly into the component instance.
You can achieve this in two ways:
Using a Hidden Component
Create a hidden component in your form and set it to not persistent (you don't want to store a value, just run) and define a custom default script to run at form rendered.
const { root } = instance;
const comp = root ? root.getComponent('key') : null;
if (comp) {
// remove any listener to avoid duplicates
comp.off('blur');
// define the on blur listener
comp.on('blur', () => {
console.log('blur');
});
}
On Form Ready
Formio.createForm(document.getElementById('formio'), form).then((formio) => {
const component = formio.getComponent('key');
if (component) {
component.on('focus', () => {
console.log('focus');
});
component.on('blur', () => {
console.log('blur');
});
}
});
Here is a working example: https://jsfiddle.net/airarrazaval/ongcuwt2/
I don't know since which version this option exists, but at least with version 4.13 the form builder has a specific property to address your concern - Validate On:
This should work perfectly fine with your custom validation code, as well.

Trigger onChange when setting State in componentDidMount?

I need to add some query paramaters to my url as a person checks off checkboxes.
I am using react router so I do something like this in my checkboxes on change event.
const stringified = queryString.stringify(parsed);
const path = `${this.props.location.pathname}?${stringified}`;
this.props.history.replace(path)
This does however seem to cause a re-render of the page(not sure if this should be happening, would prefer it not to do that so maybe I got to use something other than replace?).
I wanted to check on componentDidMount the url to see if the value is there. If it is there then I wanted to update the state of the checkbox.
#observable
isChecked = false;
#action
componentDidMount() {
const parsed = queryString.parse(this.props.location.search);
this.isChecked = parsed && parsed["param"] === this.props.option;
}
However I don't see the onChange being trigger.
Right now I have on change a function that takes the value and uses it to filter, so I need the function to run.
I could put that function in the componentDidMount but I wanted to make sure before I do that, there is nothing I am missing on why the change event is not be fired.
Try setting the state in the constructor() and incomponentDidUpdate().
When a URL parameter is added to the same route, the existing component is utilized (i.e. an update event) vs. a new one being created. As a result, you won't see a componentDidMount() event.
Another option/solution is to update the state of isChecked directly and push the history/url change.
If what you are trying to prevent is the page refresh use this built in function in your onSubmit event(if I understand your question correctly.)
event.preventDefault();
It stops the browser from auto-refreshing! Make sure to call event in your function though.
ie
onSubmit=(event)=>{
event.preventDefault();
//rest of code
}
If you are trying to filter, the es6 .filter method is useful for checkboxes. I personally used a select dropdown menu to filter the options and selectively show the ticked items in a ToDo List: "Done" "Active" "Completed" and used those states in my filter method.

Input field needs to both controlled and uncontrolled

I have an input field which is rendered like this:
class InputSection extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
handleChange(event) {
this.props.onChange(event.target.name, event.target.value);
}
render() {
return (
<input type="text"
name="topicInputText"
value={this.props.formValues['topicInputText']}
onChange={this.props.onChange}
/>
);
}
}
There are two (perceived) reasons why I have value bound to an entry of the this.props.formValues object:
I need to pass in a default value
I need the ability to reset the input field when the users clicks on a "reset" button. This reset button triggers a redux action which leads to a change in the formValues object passed-in as a prop
The onChange event handler, which is handled up the component chain, triggers a redux action which updates the store state that drives the formValues object passed-in as a prop.
But when I type into the input field using the keyboard I get this console warning:
Warning: InputSection is changing an uncontrolled input of type text
to be controlled. Input elements should not switch from uncontrolled
to controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
I suppose this is because my input component is being "managed" (since I'm binding it's value). But, if I can't bind it's value how do I set it's initial value or reset the value at a later time?
That generally means you're switching between giving it undefined and defined values. I'm guessing that formValues["topicInputText"] does not exist when this is first rendered.
You'll need to make sure that the initial value you pass in is not undefined.

How can I abstract multiple onChange events through a single React Function

I'm building a simple inventory system that uses bi-directional data flow to render a form from state, the form can then be updated by the user and changes feed back into state. So far so good. Now I'd like to simplify my code as I have a bunch of different onChange Handlers -as each form input has a unique handler. How can I simplify my code and have a single onChange handler? I tried passing the state attribute that I wish to edit through the callBacks - but this doesn't work. However if I explicitly specify the state attribute I wish to edit then it works fine.
The top example works but the bottom example doesn't. What am I missing here?
MethodUpdateInventoryState (key, event, updatedValue) {
var fishState = this.state.fishes;
var eventTarget = "name";
fishState[key].name = updatedValue;
this.setState({fishes: fishState});
}
MethodUpdateInventoryState (key, event, updatedValue) {
var fishState = this.state.fishes;
var eventTarget = "name";
fishState[key].eventTarget = updatedValue;
this.setState({fishes: fishState});
}
Gracias!

Categories

Resources