change value of input made with react from chrome extension - reactjs

I work on Chrome extension, i need to update lot of inputs of an html page made with React from numbers readed from CSV. I cannot update the web site.
-
An example of input copied from the rendered website :
<td><input class="input input_small fpInput" value="29,4"></td>
-
How it's made (not sure 100% about that, had to read the uglified js source)
{
key: "render",
value: function () {
return s.a.createElement("input", {
className: "input input_small fpInput",
value: this.state.value,
onChange: this.handleChange,
onBlur: this.handleSubmit,
onFocus: this.handleFocus
})
}
}
-
Each time you change the input value a function is called and a POST is made to save it.
I want to trigger the onBlur() or onChange() from my extension after i changed the input value to trigger the POST
I tried this :
var el = document. ... .querySelector('input'); // the selector is simplied of course
el.value = 321;
el.onChange(); // ERROR onChange is not a function
el.onchange(); // ERROR onchange is not a function
el.handleChange(); // ERROR handleChange is not a function
Any idea please ?

You can't call a React component's method directly from the DOM element it has rendered. You need to trigger an event that bubbles up so that React can catch it at the document level and process it normally, as it would do with a real one.
✨ Document.execCommand():
As pointed out by #woxxom in the comments, the easiest way to do that might be to focus the inputs and then use Document.execCommand():
const input1 = document.getElementById('input1');
const input2 = document.getElementById('input2');
input1.focus();
document.execCommand('insertText', false, 'input1');
input2.focus();
document.execCommand('insertText', false, 'input2');
<input id="input1" />
<input id="input2" />
⚒️ Manually Dispatching Events:
Otherwise, you might try manually dispatching a change, input and/or blur event using the Event() constructor in those fields after you change their value.
Also, note Event()'s second optional argument contains a field, bubbles, that is false by default. You need that one to be true. Otherwise, this won't work, as React is listening for events on the document.
Additionally, you might need to use Element.setAttribute() too, as that will update the value attribute on the DOM (the initial value on the field), while element.value will update the current value (and so the display value). Usually, though, it's not needed. For more on this see What is the difference between properties and attributes in HTML?.
This approach might have some timing issues you might need to handle using setTimeout when updating multiple inputs, so if the first one works, I'd go for that one instead.
const input1 = document.getElementById('input1');
// This updates the value displayed on the input, but not the DOM (you can do that with setAttribute,
// but it's not needed here):
input1.value = 'input1';
// Dispatch an "input" event. In some cases, "change" would also work:
input1.dispatchEvent(new Event('input', { bubbles: true }));
// It looks like only one field will be updated if both events are dispatched
// straight away, so you could use a setTimeout here:
setTimeout(() => {
const input2 = document.getElementById('input2');
input2.value = 'input2';
input2.dispatchEvent(new Event('input', { bubbles: true }));
});
<input id="input1" />
<input id="input2" />

To elaborate a bit more on #varoons answer, which is factually correct albeit a bit short on explanation.
You can do so by injecting (dispatching, in browser terms) the event into the dom:
// Needs setAttribute to work well with React and everything, just `.value` doesn't cut it
// Also changed it to a string, as all attributes are strings (even for <input type="number" />)
el.setAttribute("value", "321");
// As #wOxxOm pointed out, we need to pass `{ bubbles: true }` to the options,
// as React listens on the document element and not the individual input elements
el.dispatchEvent(new Event("change", { bubbles: true }));
el.dispatchEvent(new Event("blur", { bubbles: true }));
This will actually call all the listeners, even those made with React (as is the case in your de-uglyfied code ;)) or made with simple element.onChange = () => {...} listeners.
Example: https://codesandbox.io/s/kml7m2nn4r

el.dispatchEvent(new CustomEvent("change"))

Related

React checkbox: onChange vs onClick with readOnly [duplicate]

<input type="checkbox" onclick="onClickHandler()" onchange="onChangeHandler()" />
From within onClickHandler and/or onChangeHandler, how can I determine what is the new state of the checkbox?
The short answer:
Use the click event, which won't fire until after the value has been updated, and fires when you want it to:
<label><input type='checkbox' onclick='handleClick(this);'>Checkbox</label>
function handleClick(cb) {
display("Clicked, new value = " + cb.checked);
}
Live example | Source
The longer answer:
The change event handler isn't called until the checked state has been updated (live example | source), but because (as Tim Büthe points out in the comments) IE doesn't fire the change event until the checkbox loses focus, you don't get the notification proactively. Worse, with IE if you click a label for the checkbox (rather than the checkbox itself) to update it, you can get the impression that you're getting the old value (try it with IE here by clicking the label: live example | source). This is because if the checkbox has focus, clicking the label takes the focus away from it, firing the change event with the old value, and then the click happens setting the new value and setting focus back on the checkbox. Very confusing.
But you can avoid all of that unpleasantness if you use click instead.
I've used DOM0 handlers (onxyz attributes) because that's what you asked about, but for the record, I would generally recommend hooking up handlers in code (DOM2's addEventListener, or attachEvent in older versions of IE) rather than using onxyz attributes. That lets you attach multiple handlers to the same element and lets you avoid making all of your handlers global functions.
An earlier version of this answer used this code for handleClick:
function handleClick(cb) {
setTimeout(function() {
display("Clicked, new value = " + cb.checked);
}, 0);
}
The goal seemed to be to allow the click to complete before looking at the value. As far as I'm aware, there's no reason to do that, and I have no idea why I did. The value is changed before the click handler is called. In fact, the spec is quite clear about that. The version without setTimeout works perfectly well in every browser I've tried (even IE6). I can only assume I was thinking about some other platform where the change isn't done until after the event. In any case, no reason to do that with HTML checkboxes.
For React.js, you can do this with more readable code. Hope it helps.
handleCheckboxChange(e) {
console.log('value of checkbox : ', e.target.checked);
}
render() {
return <input type="checkbox" onChange={this.handleCheckboxChange.bind(this)} />
}
Use this
<input type="checkbox" onclick="onClickHandler()" id="box" />
<script>
function onClickHandler(){
var chk=document.getElementById("box").value;
//use this value
}
</script>
use onclick event on all checkboxes that you want to get their values whether they are checked or not.
<input type="checkbox" value="rightSideCheckbox" onclick='handleClick(this);'>
function handleClick(checkbox) {
if(checkbox.checked){
console.log(checkbox.value+"True")
}
else{
console.log(checkbox.value+"False")
}
}
const [checkboxval, setCheckboxVal] = React.useState(null);
const handleChangeCheckbox = (e) => {
setCheckboxVal(e.target.checked);
}
render() {
return <input type="checkbox" onChange={(e) => handleChangeCheckbox(e)}
}

Modify Child state from parent just once

I have created a <Form> Component that makes all the form-fields you give to it controlled by injecting value and onChange props to the form-fields by iterating through them. This has been working perfectly well for me for most of the forms I have created.
When I needed to have the functionality of the form-field values controlling some aspect of the parent state, I added a onFormValueChange prop to the Form that would get called whenever a field value gets updated. Using this I can track a subset of the changes to the Form's state.
However, now my problem is this...how do I override the value of a form-field, conditional on some event that occurs in the parent. I have not been able to figure out how to override the Form's state just once. If I give it a prop that sets an override value like {name: string, value: any}, then on every update this override value will override the form-field's value which is not good.
These are the solutions I thought of but they seem extremely hacky and I was hoping someone in the SO community can help.
Set an override prop on the Form Component which times out after around 100ms and hope that the user doesn't try to modify the form in that tiny duration. But I dislike using setTimeout for hacks like these.
Pass a disableOverride function along with the overrideValue prop. Then in my Form's shouldComponentUpdate I can just call disableOverride() in the callback of the setState I will use to override the value. Something like:
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.override) {
const { name, value } = nextProps.override;
const newState = Object.assign({}, this.state, { [name]: value });
this.setState(newState, () => {
nextProps.disableOverride();
});
return false;
}
return true;
}
But this also feels unnecessarily complicated, and possibly quite vulnerable to crashing unexpectedly.
EDIT Some further clarification: The point of this would be so that for example if I have 'country' and 'city' fields then if 'country' is cleared, then the 'city' should get cleared too. I can track the state of 'country' with onFormValueChange but don't have an API to modify the Form state in order to clear the 'city' field.
I came up with a solution. It was absurdly simple, I dont know why it took me so long.
componentDidUpdate(prevProps, prevState) {
if (!isEqual(prevProps.override, this.props.override)) {
const { name, value } = nextProps.override;
this.setState({
[name]: value
});
}
}
isEqual is an object comparison function taken from lodash

Dynamically set a property value for an Input in Semantic UI React

I have an Input element that I want to display an error on when the form validation fails.
<Input ref="amount" error={false} />
When the user enters an incorrect amount, I want to change "error" to "true". How can this be done?
I have tried:
this.refs.amount.props.error = true;
Which seems bad but I'm not sure how else. If I add a conditional statement in the definition of the Input element, that seems to only evaluate once and then remain the same. Do I need to force an update on the element? If so, how?
Yes it's possible to validate the input when the form is submitted.
All you need is to keep track on input value and use same approach as #SajithDilshan for the input error.
this.state = {
error: false,
value: ''
}
...
render(){
return
...
<Input
ref="amount"
value={this.state.value}
error={this.state.error}
/>
...
}
Then onSubmit should looks like:
onSubmit(e){
const isError = this.state.value === '';
this.setState({error: isError});
// rest of your logic
}
Hope it will help!
Use the onChange() method on the input as below.
<Input ref="amount" onChange={this.onInputChange} error={this.state.error} />
After that implement the onInputChange() method as below in your component.
onInputChange = (e) => {
if (e.target.value === "") { // logic to validate the input
this.setState({error: true});
} else {
this.setState({error: false});
}
}
Note that this will add error property to the state.
Further, you should not modify the props within a component. Props are passes from parent component to the child component as immutable inputs.
This is not exactly the answer, but still:
This type of fiddling with each possible state of form element (valid, invalid, warning, show tooltip, was edited, in focus, left focus, was submitted, submit failed or not, etc) becomes to much trouble when the form grows beyond 1 input field.
I would suggest to use redux-form package that integrates with semantic-ui-react` almost perfectly and provided you have provided it with the validate function does everything else for you. It takes some time to understand the basics of it, but it really pays.

Bubble is not firing when programmatically Input changed

Having an Input from semantic-ui-react changed when the User writes something, does fire onChange and any parent component that includes the Input fires onChange as well. A process called bubble .... right?
Example
<li onChange={this.onListChange}>
<Input onChange={this.onInputChange}/>
</li>
Any change made in the Input, fires onInputChange() and onListChange() together one after another.
Although, when Input.value/text/content is changed dynamically, Input.onChange() is not called at all. So you have to call the onChange() function manually.
setFoo() {
this.input.value = 'foo';
this.inputChange();
}
But the problem here is, that when we call inputChange we don't get as e.target the component Input, nor data has anything to do with it, and the most important ... :
No Bubbling effect.
Which means that onListChange() will not be fired.
Question:
How can I achieve a proper bugless bubbled onChange() on Semantic-UI-React?
Not exactly sure about semantic-ui-react because I've never used it, but this in theory should work (it works for a normal input):
setFoo() {
this.input.value = 'foo';
const event = new Event('input', { bubbles: true });
this.input.dispatchEvent(event);
}

What is the best way to trigger change or input event in react js from jQuery or plain JavaScript

We use Backbone + ReactJS bundle to build a client-side app.
Heavily relying on notorious valueLink we propagate values directly to the model via own wrapper that supports ReactJS interface for two way binding.
Now we faced the problem:
We have jquery.mask.js plugin which formats input value programmatically thus it doesn't fire React events. All this leads to situation when model receives unformatted values from user input and misses formatted ones from plugin.
It seems that React has plenty of event handling strategies depending on browser. Is there any common way to trigger change event for particular DOM element so that React will hear it?
For React 16 and React >=15.6
Setter .value= is not working as we wanted because React library overrides input value setter but we can call the function directly on the input as context.
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');
var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);
For textarea element you should use prototype of HTMLTextAreaElement class.
New codepen example.
All credits to this contributor and his solution
Outdated answer only for React <=15.5
With react-dom ^15.6.0 you can use simulated flag on the event object for the event to pass through
var ev = new Event('input', { bubbles: true});
ev.simulated = true;
element.value = 'Something new';
element.dispatchEvent(ev);
I made a codepen with an example
To understand why new flag is needed I found this comment very helpful:
The input logic in React now dedupe's change events so they don't fire
more than once per value. It listens for both browser onChange/onInput
events as well as sets on the DOM node value prop (when you update the
value via javascript). This has the side effect of meaning that if you
update the input's value manually input.value = 'foo' then dispatch a
ChangeEvent with { target: input } React will register both the set
and the event, see it's value is still `'foo', consider it a duplicate
event and swallow it.
This works fine in normal cases because a "real" browser initiated
event doesn't trigger sets on the element.value. You can bail out of
this logic secretly by tagging the event you trigger with a simulated
flag and react will always fire the event.
https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128
At least on text inputs, it appears that onChange is listening for input events:
var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
Expanding on the answer from Grin/Dan Abramov, this works across multiple input types. Tested in React >= 15.5
const inputTypes = [
window.HTMLInputElement,
window.HTMLSelectElement,
window.HTMLTextAreaElement,
];
export const triggerInputChange = (node, value = '') => {
// only process the change on elements we know have a value setter in their constructor
if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {
const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
const event = new Event('input', { bubbles: true });
setValue.call(node, value);
node.dispatchEvent(event);
}
};
I know this answer comes a little late but I recently faced a similar problem. I wanted to trigger an event on a nested component. I had a list with radio and check box type widgets (they were divs that behaved like checkboxes and/or radio buttons) and in some other place in the application, if someone closed a toolbox, I needed to uncheck one.
I found a pretty simple solution, not sure if this is best practice but it works.
var event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': false
});
var node = document.getElementById('nodeMyComponentsEventIsConnectedTo');
node.dispatchEvent(event);
This triggered the click event on the domNode and my handler attached via react was indeed called so it behaves like I would expect if someone clicked on the element. I have not tested onChange but it should work, and not sure how this will fair in really old versions of IE but I believe the MouseEvent is supported in at least IE9 and up.
I eventually moved away from this for my particular use case because my component was very small (only a part of my application used react since i'm still learning it) and I could achieve the same thing another way without getting references to dom nodes.
UPDATE:
As others have stated in the comments, it is better to use this.refs.refname to get a reference to a dom node. In this case, refname is the ref you attached to your component via <MyComponent ref='refname' />.
You can simulate events using ReactTestUtils but that's designed for unit testing.
I'd recommend not using valueLink for this case and simply listening to change events fired by the plugin and updating the input's state in response. The two-way binding utils more as a demo than anything else; they're included in addons only to emphasize the fact that pure two-way binding isn't appropriate for most applications and that you usually need more application logic to describe the interactions in your app.
For HTMLSelectElement, i.e. <select>
var element = document.getElementById("element-id");
var trigger = Object.getOwnPropertyDescriptor(
window.HTMLSelectElement.prototype,
"value"
).set;
trigger.call(element, 4); // 4 is the select option's value we want to set
var event = new Event("change", { bubbles: true });
element.dispatchEvent(event);
I stumbled upon the same issue today. While there is default support for the 'click', 'focus', 'blur' events out of the box in JavaScript, other useful events such as 'change', 'input' are not implemented (yet).
I came up with this generic solution and refactored the code based on the accepted answers.
export const triggerNativeEventFor = (elm, { event, ...valueObj }) => {
if (!(elm instanceof Element)) {
throw new Error(`Expected an Element but received ${elm} instead!`);
}
const [prop, value] = Object.entries(valueObj)[0] ?? [];
const desc = Object.getOwnPropertyDescriptor(elm.__proto__, prop);
desc?.set?.call(elm, value);
elm.dispatchEvent(new Event(event, { bubbles: true }));
};
How does it work?
triggerNativeEventFor(inputRef.current, { event: 'input', value: '' });
Any 2nd property you pass after the 'event' key-value pair, it will be taken into account and the rest will be ignored/discarded.
This is purposedfully written like this in order not to clutter arguments definition of the helper function.
The reason as to why not default to get descriptor for 'value' only is that for instance, if you have a native checkbox <input type="checkbox" />, than it doesn't have a value rather a 'checked' prop/attribute. Then you can pass your desired check state as follows:
triggerNativeEventFor(checkBoxRef.current, { event: 'input', checked: false });
I found this on React's Github issues: Works like a charm (v15.6.2)
Here is how I implemented to a Text input:
changeInputValue = newValue => {
const e = new Event('input', { bubbles: true })
const input = document.querySelector('input[name=' + this.props.name + ']')
console.log('input', input)
this.setNativeValue(input, newValue)
input.dispatchEvent(e)
}
setNativeValue (element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set
const prototype = Object.getPrototypeOf(element)
const prototypeValueSetter = Object.getOwnPropertyDescriptor(
prototype,
'value'
).set
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value)
} else {
valueSetter.call(element, value)
}
}
Triggering change events on arbitrary elements creates dependencies between components which are hard to reason about. It's better to stick with React's one-way data flow.
There is no simple snippet to trigger React's change event. The logic is implemented in ChangeEventPlugin.js and there are different code branches for different input types and browsers. Moreover, the implementation details vary across versions of React.
I have built react-trigger-change that does the thing, but it is intended to be used for testing, not as a production dependency:
let node;
ReactDOM.render(
<input
onChange={() => console.log('changed')}
ref={(input) => { node = input; }}
/>,
mountNode
);
reactTriggerChange(node); // 'changed' is logged
CodePen
well since we use functions to handle an onchange event, we can do it like this:
class Form extends Component {
constructor(props) {
super(props);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.state = { password: '' }
}
aForceChange() {
// something happened and a passwordChange
// needs to be triggered!!
// simple, just call the onChange handler
this.handlePasswordChange('my password');
}
handlePasswordChange(value) {
// do something
}
render() {
return (
<input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} />
);
}
}
The Event type input did not work for me on <select> but changing it to change works
useEffect(() => {
var event = new Event('change', { bubbles: true });
selectRef.current.dispatchEvent(event); // ref to the select control
}, [props.items]);
This ugly solution is what worked for me:
let ev = new CustomEvent('change', { bubbles: true });
Object.defineProperty(ev, 'target', {writable: false, value: inpt });
Object.defineProperty(ev, 'currentTarget', {writable: false, value: inpt });
const rHandle = Object.keys(inpt).find(k => k.startsWith("__reactEventHandlers"))
inpt[rHandle].onChange(ev);
A working solution can depend a bit on the implementation of the onChange function you're trying to trigger. Something that worked for me was to reach into the react props attached to the DOM element and call the function directly.
I created a helper function to grab the react props since they're suffixed with a hash like .__reactProps$fdb7odfwyz
It's probably not the most robust but it's good to know it's an option.
function getReactProps(el) {
const keys = Object.keys(el);
const propKey = keys.find(key => key.includes('reactProps'));
return el[propKey];
}
const el = document.querySelector('XX');
getReactProps(el).onChange({ target: { value: id } });
Since the onChange function was only using target.value I could pass a simple object to onChange to trigger my change.
This method can also help with stubborn react owned DOM elements that are listing for onMouseDown and do not respond to .click() like you'd expect.
getReactProps(el).onMouseDown(new Event('click'));
If you are using Backbone and React, I'd recommend one of the following,
Backbone.React.Component
react.backbone
They both help integrate Backbone models and collections with React views. You can use Backbone events just like you do with Backbone views. I've dabbled in both and didn't see much of a difference except one is a mixin and the other changes React.createClass to React.createBackboneClass.

Resources