30

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));
  }
}
Preview
  • 35,317
  • 10
  • 92
  • 112
Ovi
  • 2,620
  • 7
  • 34
  • 51
  • 2
    React is usign virtual DOM so mixing it with JQuery which is DOM manipulation would not be a good practice. Try to construct Data Model and interpolate it with View then any time you change model with controller will automatically reflect to view. And this is what we call MVC in front-end – Sajed Apr 16 '17 at 15:37
  • Thanks @SaJed, unfortunately I don't have access to the virtual DOM, so I was looking into a way to trigger its refresh from the browser. – Ovi Apr 18 '17 at 10:14
  • can you please explain why you dont have access to the virtual DOM? Are you not the original developer and you are trying to do some clientside-only automation? – swyx Apr 20 '17 at 00:11
  • 1
    exactly @swyx, I'm not the developer, it's just client-side automation. – Ovi Apr 20 '17 at 19:13
  • i just tried: $(".class-input")[0].value = "woah"; and it worked... don't know about events... but value works fine – Lojka Apr 21 '17 at 12:46
  • can you post the code pieces of your component rendering the input. Also accessing from jQuery/plain js depends on the scenario. can you post your scenario or a similar one ? – Panther Apr 21 '17 at 22:51

11 Answers11

35

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

Anand Singh
  • 1,091
  • 13
  • 21
15

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.

Preview
  • 35,317
  • 10
  • 92
  • 112
  • 1
    A github thread about this for various react versions: https://github.com/facebook/react/issues/11488 – Ryanman Oct 15 '19 at 13:27
  • Hello Balthaza and @Ryanman, I'm also trying to do same thing and followed what you have suggested. However, the react login form has validators which I'm not able to statisfy because of which I'm stuck. This is my question - https://stackoverflow.com/q/62540621/819866 Appreciate your help in advance. – Sunil Kumar Jun 25 '20 at 02:51
4

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;
}
javabeangrinder
  • 6,939
  • 6
  • 35
  • 38
2

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);
Milan
  • 145
  • 8
1

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
Community
  • 1
  • 1
blackmiaool
  • 5,234
  • 2
  • 22
  • 39
0

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"
/>
Vinicius Vieira
  • 398
  • 8
  • 18
0

I've found a little simpler way, similar to the first answer

var input = document.querySelector("form input");
Object.defineProperty(input, "value", {
  value: "programmatically added value",
  writable: true
});

var inputEvent = new Event('input', { bubbles: true});
input.dispatchEvent(inputEvent);
Marian07
  • 2,303
  • 4
  • 27
  • 48
-1

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;
Carlos Querioz
  • 277
  • 3
  • 7
-2

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});
}
Dalin Huang
  • 11,212
  • 5
  • 32
  • 49
Dani Akash
  • 6,828
  • 3
  • 35
  • 47
  • thanks @Dani! unfortunately I don't have access to the virtual DOM, so I was looking into a way to trigger its refresh from the browser. – Ovi Apr 19 '17 at 23:08
  • I don't understand how you are not having access to the virtual DOM. Are you using the page as an iframe? – Dani Akash Apr 20 '17 at 01:39
  • 1
    I'm injecting the script that needs to modify the actual DOM through a browser extension. – Ovi Apr 20 '17 at 13:33
-3

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.

pirs
  • 2,410
  • 2
  • 18
  • 25
-3

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

  1. First I render the Parent component I created. The parent component contains a button and another React component I created InputWithButton
  2. The Parent constructor gets called first, setting the Parent components state to the object {value: "initial value"}
  3. 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.
  4. The Parent component passes its state.value as a prop to the InputWithButton component.
  5. 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.
  6. 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.