4

I am writing an userscript and I can't manage to fill a form made by reactjs. My code:

document.querySelector("#id-username").value = "name@domain.xx";
// Attempt to notify framework using input event
document.querySelector("#id-username").dispatchEvent(new Event("input", {data:"name@domain.xx"}));
// Attempt to notify framework using change event
document.querySelector("#id-username").dispatchEvent(new Event("change"));
// This doesn't help either
document.querySelector("#id-username").dispatchEvent(new Event("blur"));
// Submit the form using button (it's AJAX form)
document.querySelector("fieldset div.wrap button").click();

I entered this code into developper tools console after loading the page. However the form ignores my programatical input:

image description

The form can be found here. The purpose of my work is to automate login to given website. I provided the specific URL, but I expect generic solution to this problem (eg. using some reactjs API) that can be applied to any reactjs form. Other users might need this solution for writing automated tests for their sites.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • 1
    There shouldn't be anything special about it being a react form. You might just want to wait for document.load to have fired before trying to automate. For example, I ran your first line from the console and it populated the username box just fine. – Keith Rousseau Mar 14 '17 at 19:46
  • @KeithRousseau I tested this in console in firefox (long after loading). The results you see are not from userscript, I didn't even start writing it. I just tested those commands after loading the page and it failed already. – Tomáš Zato Mar 14 '17 at 21:55

2 Answers2

8

For Chrome version 64, there is a workaround. Otherwise the event is being ignored.

See:https://github.com/facebook/react/issues/10135#issuecomment-314441175

(Full credit to fatfisz in the link)

Code

function 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);
  }
}

Usage

setNativeValue(textarea, 'some text');
// you must dispatch the input event, or the value will not update !!!
textarea.dispatchEvent(new Event('input', { bubbles: true }));
Walter Stabosz
  • 7,447
  • 5
  • 43
  • 75
bschwagg
  • 779
  • 7
  • 9
  • 1
    This worked for me when accepted answer did not. Chrome v66 – Walter Stabosz May 15 '18 at 05:05
  • I wonder what the problem is. Does chrome not emit the event. Or does react check if the event is trusted? – Tomáš Zato May 15 '18 at 16:56
  • I don't know very much about the internal workings of React,but I suspect that if you do not dispatch the event, then React does not know to update the value in the virtual DOM, and so when you refocus on the `input` element, React sets its value back to what it has in the virtual DOM. – Walter Stabosz May 17 '18 at 11:06
7

Event must be emmited to ReactJS to make it register the value. In particular, the input event. It is really important to ensure that the event bubbles - React JS has only one listener at the document level, rather than on the input field. I crafted the following method to set the value for input field element:

function reactJSSetValue(elm, value) {
    elm.value = value;
    elm.defaultValue = value;
    elm.dispatchEvent(new Event("input", {bubbles: true, target: elm, data: value}));
}
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • This was exactly what I was looking for. One correction: your target param in the Event needs to be changed to elm instead of username. – funkju Sep 08 '17 at 15:42
  • 1
    There is a workaround for this solution in cases where it may not work: https://github.com/facebook/react/issues/11488#issuecomment-347775628 Set the event .simulated field to true and I was able to get my chrome extension working against a facebook target. – Buddy Dec 31 '18 at 01:23
  • 1
    This no longer works for newer versions of React, please see this post for latest implementation: https://stackoverflow.com/a/59939017/432556 – guowy Jun 03 '21 at 04:34