Set React Input Field Value from JavaScript or JQuery - javascript

How can you programmatically set the value of an input field generated by React, either with vanilla JS or JQuery?
I've tried the following and nothing seems to work.
$(obj).val('abc');
$(obj).attr('value', 'abc');
$(obj).keydown();
$(obj).keypress();
$(obj).keyup();
$(obj).blur();
$(obj).change();
$(obj).focus();
I've also tried to simulate keyPress (as suggested here) events but it doesn't seem to work either.
simulateKeyPresses (characters, ...args) {
for (let i = 0; i < characters.length; i++) {
this.simulate('keyPress', extend({
which: characters.charCodeAt(i),
key: characters[i],
keyCode: characters.charCodeAt(i)
}, args));
}
}

Out of all the answers and after a lot of googling, I found this to be working
function changeValue(input,value){
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
).set;
nativeInputValueSetter.call(input, value);
var inputEvent = new Event("input", { bubbles: true });
input.dispatchEvent(inputEvent);
}
We are using window.HTMLInputElement.prototype that is HTMLInputElement. An interface that provides special properties and methods for manipulating the options, layout, and presentation of input elements.
Then we will use Object.getOwnPropertyDescriptor() method to set input value. Last we will dispatch change event on the input to simulate with React onChange
Here is a detailed explanation of this answer: https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04

As showcased in the react test utils docs in the simulate section, you can see what they're basically doing is changing the DOM node value and then triggering an input event.
What you could do is something like the following, calling it with your input DOM element and new value.
const changeValue = (element, value) => {
const event = new Event('input', { bubbles: true })
element.value = value
element.dispatchEvent(event)
}
Depends on how you defined your components though, if for example you're expecting an enter keypress, you'll have to dispatch the matching event.

This is a well tested solution that works for IE11 as well as other browsers. It is the createNewEvent that differentiate this solution form the others in here I guess. The setReactValue method also returns the changed value.
function setReactValue(element, value) {
let lastValue = element.value;
element.value = value;
let event = createNewEvent("input", element);
event.simulated = true;
let tracker = element._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
element.dispatchEvent(event);
}
return lastValue;
}
function createNewEvent(eventName, element) {
let event;
if (typeof(Event) === 'function') {
event = new Event(eventName, {target: element, bubbles:true});
} else {
event = document.createEvent('Event');
event.initEvent(eventName, true, true);
element.addEventListener(eventName, function(e) {
e.target = element;
});
}
return event;
}

This will depend on the browser, but for text inputs the onChange call is listening to input events
element.value = 'new value';
var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);

According to this answer, you can get react instance from dom.
Assume the obj is a dom element.
function findReact(dom) {// from https://stackoverflow.com/a/39165137/4831179
for (var key in dom) {
if (key.startsWith("__reactInternalInstance$")) {
var compInternals = dom[key]._currentElement;
var compWrapper = compInternals._owner;
var comp = compWrapper._instance;
return comp;
}
}
return null;
};
var instance = findReact(obj);
console.log(instance.state);//try to modify the form and check what's here
instance.setState({
//the state name from the previous step
});
instance.submit();//something like this

You can achieve this by using ReactDOM(https://facebook.github.io/react/docs/react-dom.html) and Jquery, is not very common to manipulate like this but it works:
var ctx = this;
//Save the context of your class to the variable ctx, since inside $/Jquery the this is a reference to $/Jquery itself.
$(ReactDOM.findDOMNode(ctx.refs.myInput)).val('abc');
And your input must have a ref property to React find it:
<input type="text"
className="form-control"
ref="myInput"
placeholder="text"
/>

I had the same problem here using React inside another framework built with JQuery.
But in my case, I was needed to change only one field. Please, check if works for you:
import React, { useEffect, useRef } from 'react';
const Exemple = () => {
const [value, setValue] = useState();
const inputRef = useRef(null);
useEffect(() => {
const myInputRef = inputRef.current;
myInputRef.onchange = e => setValue(e.target.value)
}, [])
return (
<div>
<input ref={inputRef} id={my_id} />
</div>
);
}
export default Exemple;

You can use the state to directly update the value of your text field.
Let the value of text input in the state be:
state = {
textInputValue: ""
};
This is how you define your text input in React
<input type="text"
className="form-control"
name="my-text-input"
placeholder="text"
value={this.state.textInputValue}
onChange={this.onTextInputChange}
/>
Once you have defined your text input, you can update the value of your text input by just changing your state say this.setState({textInputValue: 'MyText'}) from within your react component. After that, you can normally update the value of the text field using
onTextInputChange(event) {
let newText = event.target.value;
return this.setState({textInputValue: newText});
}
I don't know what kind of scenario you are facing. Since React creates and maintains it's own virtual DOM, you can't manipulate the DOM elements with Jquery or Javascript from outside React. However if you need to get data from outside, use componentWillMount() in your React component to write code that gets data from your required data source and set it to the state of your TextInput
componentWillMount() {
// Code to get your data into variable 'defaultTextValue'
this.setState({textInputValue: defaultTextValue});
}

Try to reassign the entire html content like
$("html").on("DOMNodeInserted DOMNodeRemoved change", "body", function(){
$("body").html($("body").html());
});
// or call a simple function with $("body").html($("body").html());
I did that to reassign html and apply events again on svg tags in jquery after raw code injection ... maybe that ll work for this case too..
Try .on() method on the events either.

I've made a codepen with a working example of what I believe Dani Akash was trying to say. It is important to know that in React, setState() causes the component to rerender, hence in my example passing the new state as a prop to the child component.
https://codepen.io/tskjetne/pen/mmOvmb?editors=1010
First I render the Parent component I created.
The parent component contains a button and another React component I created InputWithButton
The Parent constructor gets called first, setting the Parent components state to the object {value: "initial value"}
The setValueInParent is a click handler I bind to the button in the Parent component. It sets the Parent components state which causes a rerender.
The Parent component passes its state.value as a prop to the InputWithButton component.
The InputWithButton component is very similar to the parent. Although, in the constructor it sets the state value to be the value prop passed in from the parent.
Other than that the InputWithButton component works more or less the same way as the Parent component.
This enables you to change the input value by typing in the input field, clicking a button in the same component as the input field, and passing in a new value as a prop from a parent.

Related

How to prevent useState variable to get reset every time my react components handles an event hook set with useEffect?

I would like a react component to handle keyboard inputs. I have attached a simplified version of my component (that shows the problem I am having) below.
The problem is: The state variable inputText does not get updated. It appears that it gets set back to the initial value "" every time I press a key. My assumption is that my component gets re-rendered every time I press a key. But why?
When using a "traditional" input element, this approach works perfectly fine. I have found many examples where the value of an input element is set to a state variable that is updated when the user types text into the input element (and the onChange event fires).
What I am doing wrong?
import './TestInput.css'
const { useState, useEffect } = require('react');
const TestInput = ( props ) => {
const [ inputText, setInputText ] = useState("");
useEffect(() => {
window.addEventListener('keypress', handleKeyPress);
console.log('Event listener added')
return () => {
window.removeEventListener('keypress', handleKeyPress);
console.log('Event listener removed');
}
}, []);
function handleKeyPress(e) {
setInputText(inputText + e.key);
}
return (
<div id="container">
<div id="input"></div>
</div>
)
}
export default TestInput;
Because you are using useEffect with empty array dependencies so window.addEventListener only call once time with handleKeyPress in the first render. And in this function, you are using directly state inputText and it still has value is the initial value '' and doesn't update to new state when event keypress was called.
The easiest way to fix is you don't use directly state inputText and pass the function to setInputText:
function handleKeyPress(e) {
setInputText(preState => preState + e.key);
}

Is there a way I can dynamically bind a string and the text it outputs without using setInterval?

Is there a way I can dynamically bind a string and the text it outputs without using setInterval? I want it to be similar to Angular and Vue though I want to do this with vanilla JS. I want to be able to open the console and change the value at any time and see the change output on my element. Thank you in advance!
I think your only two options are:
A. Edit the element directly, e.g.
myPublicElemeVariable.innerText = 'Bla'
B. Use a setter (or Proxy):
obj = {
get str() { return this.myStr; }
set str(val) {
elem.innerText = val;
this.myStr = val;
}
}
C. Just use a function/method!
If you mean you want change to be event-driven, there is already a very simple event framework in javascript - the EventTarget class as demonstrated by this Code Sandbox
//define a watchable thing
class ValueTarget extends EventTarget {
constructor(value = "") {
super();
this.setValue(value);
}
getValue() {
return this._value;
}
setValue(value) {
this._value = value;
this.dispatchEvent(new CustomEvent("change", { detail: { value } }));
}
}
//get the page elements
const inputElement = document.querySelector("input");
const outputElement = document.querySelector("h1");
//create a watchable thing
const fieldTarget = new ValueTarget("");
//wire events that will change the watchable
inputElement.addEventListener("input", function (e) {
fieldTarget.setValue(e.target.value);
});
//receive notifications from the watchable
fieldTarget.addEventListener("change", (e) => {
outputElement.textContent = e.detail.value;
});
You may be as well to build your own given how simple it is - maintains a list of listeners and calls them when notified. My work recently needed such a thing which I knocked up in Typescript at https://github.com/cefn/lauf/blob/main/modules/lauf-store/src/core/watchable.ts#L3-L21 and would therefore be very easy to redo in javascript.

Modify reactjs form input value outside of react

I'm using a js lib that uses react under the hood. I want to modify it to prefill an input on a form that it renders. I only have access to the top level react component from instantiation. How can I set the value such that react picks it up?
I've tried setting el.value and $el.attr('value', 'val') to no avail. I've also tried setting the state directly with el.__reactInternalInstance$abc123._currentElement.props.value but doesn't work either.
Calling:
const event = new Event("input", { bubbles: true })
el.dispatchEvent(event)
hasn't helped either.
I came up with a solution using jquery, couldn't get it to work without a library. The first line fills the field when the user clicks the input. The second fills it when they expand operation. Note that one gets called on expanding any operation. But jquery seems to work just fine with on click. Part of the problem is that the elements are created until the operation is expanded.
$('body').on('click', 'tr[data-param-name="<INPUT NAME>"] input', setInput)
$('.opblock').on('click', setInput)
I don't have the link for where I found the below call but this seems to set the value in a way that gets picked up by react.
function setInput (e) {
let xinput = $('tr[data-param-name="<INPUT NAME>"] input')
let target = xinput[0]
if (xinput.val() == '') {
let value = "INPUT VALUE"
const setter = Object.getOwnPropertyDescriptor(target, "value").set
const prototype = Object.getPrototypeOf(target)
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set
if (setter && setter !== prototypeValueSetter) {
prototypeValueSetter.call(target, value)
} else {
setter.call(target, value)
}
const event = new Event("input", {bubbles: true})
target.dispatchEvent(event);
}
}

How can I bind a text box to a complex object within state in React?

Background
I am building an office add-in using their React-based starter kit and TypeScript. I mention this because I am unable to get great debug support as far as I can tell, so I'm unable to see the error message that React is providing in my current situation.
What I'm attempting
(simplifying below. I can be more specific if you'd like; let me know.)
I have an interface for my AppState, and a complex object with some properties:
export interface AppState {
eventInput: EventInput;
}
export class BookendEventInput {
public minutesAdjacent: number = 30;
public subject: string = "";
public enabled: boolean = false;
public eventId: string = "";
}
I have one working scenario, which is a checkbox:
<Checkbox id="enableBookendBefore" checked={this.state.eventInput.enabled} onChange={this.eventInputCheckboxChanged}></Checkbox>
That is updating the state via the change function:
eventInputCheckboxChanged = () => {
this.setState((state: AppState) => {
var newValue = !this.state.eventInput.enabled;
var input = state.eventInput;
input.enabled = newValue;
state.eventInput = input;
})
}
But this isn't working for another scenario.
The Problem
I am now attempting to do something similar with a textbox. I have an input:
<input type="text" id="subject" disabled={!this.state.eventInput.enabled} value={this.state.eventInput.subject} onChange={this.subjectChanged} />
And the change function:
subjectChanged = (e) => {
var newSubject = e.target.value;
this.setState((state: AppState)=> {
var input = state.eventInput;
input.subject = newSubject;
state.eventInput = input;
})
Expected Behavior: I would expect to see the subject text box & state updated, as if they were two-way bound.
Actual Behavior: The entire screen goes blank/white, indicating that I'm getting a React-level error I believe (since I can't do F12 and can't see debug output due to it being in a task pane inside Outlook.)
The Question
How can I correctly bind a textbox using React, that's tied to a property in an object within state? Is it possible to do this, or am I violating a React principle?
In this case, you're using the callback to setState to try and modify state. This is either not firing or causing an infinite loop, I'm unsure of which!
Either way, to correctly modify state you'll want:
subjectChanged = (e) => {
var newSubject = e.target.value;
var input = state.eventInput;
input.subject = newSubject;
this.setState({eventInput: input});
});
This will achieve what you're looking for.

reactjs input element loses focus after keystroke

So I am using a hash to store the values of dynamically created rows of input values and I lose focus on the input I am modifying after entering only one character. I think the solution to this may be to use refs to refocus on only the last input changed, but I couldn't get it to work, as I wasn't able to figure out how to specify which element was last changed. Advice on how to solve this is appreciated.
The code below dynamically creates input boxes, and looks up their values based on the unitPriceValueHash. Each variant has an id, and id is used as the key to the hash.
I created a codepen to try and recreate the problem, but the issue im facing doesn't show up in code pen. In my actual app I press 1 for example in the input box, then the cursor is not on the input box anymore.
https://codepen.io/ByteSize/pen/oogLpE?editors=1011
The only difference between the codepen and my code appears to be the fact the the inputs are nested inside a table.
CreateItem(variant) {
const unitPriceValueHash = this.props.unitPriceValueHash
return {
variant_title: variant.variant_title,
variant_price: variant.variant_price,
unit_cost: <TextField
type="number"
onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
key={variant.id}
value={unitPriceValueHash[variant.id] || ''}
/>
};
}
Below is the change of state that modifies the hash
handleUnitPriceChange (id, event) {
const unitPriceValueHash = this.state.unitPriceValueHash
unitPriceValueHash[id] = event
console.log(unitPriceValueHash)
this.setState({unitPriceValueHash: unitPriceValueHash});
//this.updateVariantUnitCost(id, event);
}
There's a couple problems with the code you've shared.
Don't use inline functions. Each render, the function is created again which means that when react compares the props, it looks like the function is different (it is a new/different function each time!) and react will re-render.
Don't modify any objects which exist in the state, instead create a new object. If you modify an object that exists in the state, you're essentially saying you don't want renders to be consistent and reproducible.
I've re-posted your original code with the issues highlighted
CreateItem(variant) {
const unitPriceValueHash = this.props.unitPriceValueHash
return {
variant_title: variant.variant_title,
variant_price: variant.variant_price,
unit_cost: <TextField
type="number"
onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
// ^^^^ - inline functions cause react to re-render every time, instead - create a component
key={variant.id}
value={unitPriceValueHash[variant.id] || ''}
/>
};
}
handleUnitPriceChange(id, event) {
const unitPriceValueHash = this.state.unitPriceValueHash
unitPriceValueHash[id] = event
// ^^^^ - please, please - don't do this. You can't mutate the state like this.
// instead, do the following to create a new modified object without modifying the object in the state
const unitPriceValueHash = Object.assign({}, this.state.unitPriceValueHash, { id: event });
this.setState({ unitPriceValueHash: unitPriceValueHash });
}
In regards to the inline-function, generally the recommendation is to create a new component for this which takes the value as a prop. That might look like this:
class UnitCost extends PureComponent {
static propTypes = {
variantId: PropTypes.number,
variantValue: PropTypes.object,
onUnitPriceChange: PropTypes.func,
}
handleUnitPriceChange(e) {
this.props.onUnitPriceChange(this.props.variantId, e)
}
render() {
return (
<TextField
type="number"
onChange={this.handleUnitPriceChange}
value={this.props.variantValue || ''}
/>
);
}
}
CreateItem(variant) {
const unitPriceValueHash = this.props.unitPriceValueHash
return {
variant_title: variant.variant_title,
variant_price: variant.variant_price,
unit_cost: (
<UnitCost
key={variant.id}
variantId={variant.id}
variantValue={unitPriceValueHash[variant.id]}
onUnitPriceChange={this.handleUnitPriceChange}
/>
),
};
}
Regarding your concerns about focus, react generally won't lose your object focus when re-rendering, so don't ever, ever re-focus an object after an update for this reason.
The only time react will lose focus, is if it completely discards the current DOM tree and starts over from scratch. It will do this if it thinks a parent object has been replaced instead of modified. This can happen because of a missing key prop, or a key prop that has changed.
You have not posted enough code for us to investigate this further. If you want more help you should build a minimum reproducible example that we can run and test.
The solution to this problem had me use an intermediate state to store the value of the input field on change, and a submit AJAX request on an onBlur
class TextFieldWrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.variantValue[this.props.variantId] || '',
}
this.handleUnitPriceChange = this.handleUnitPriceChange.bind(this)
this.updateValue = this.updateValue.bind(this)
}
updateValue(value){
this.setState({
value: value,
});
}
handleUnitPriceChange() {
this.props.onUnitPriceChange(this.props.variantId, this.state.value);
}
render(){
return (
<TextField
type="number"
id={this.props.variantId}
key={this.props.variantId}
onChange={this.updateValue}
onBlur={this.handleUnitPriceChange}
value={this.state.value}
/>
);
}
}

Categories