3

I have two components: ParentComponent and ChildComponent.

The ChildComponent has an input[type="text"] element that when it changes its text that event is propagated to the ParentComponent through the event change and the onChange listener.

The code below is a simplification of a bigger problem, that's why you will see some requirements highlighted there.

My problem is that I need to trigger the change event inside the function: handleClick. I did some experiments with no luck.

Here you have the code sandbox you can experiment with (please provide a fork with your approach):

https://codesandbox.io/s/wqw49j5krw

Here you have the code:

ParentComponent.js

import React from "react";
import ChildComponent from "./ChildComponent";

export default class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "Peter"
    };
  }
  handleChange = event => {
    let target = event.target;
    let value = target.value;
    this.setState({
      name: value
    });
  };
  render() {
    return (
      <div>
        <ChildComponent value={this.state.name} onChange={this.handleChange} /><br />
        <span>Hello</span>&nbsp;
        <span>{this.state.name}!</span>
      </div>
    );
  }
}

ChildComponent.js

import React from "react";
import ReactDOM from "react-dom";

export default class ChildComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: this.props.value
    };
  }
  handleChange = event => {
    const name = event.target.value;
    this.setState({ value: name });
    if (this.props.onChange !== undefined) {
      this.props.onChange(event);
    }
  };
  handleClick = name => {
    const inputName = this.refs.name;
    console.log('Name before being changed: ' + inputName.value); // this works
    // PROBABLY HERE IS WHERE THE CODE NEEDS TO BE MODIFIED
    this.setState({ value: name });
    var event = new Event('input', { bubbles: true });
    inputName.dispatchEvent(event); // this doesn't propagate the event to the parent
  };
  render() {
    return (
      <div>
        {"Your name: "}
        <input type="text"
          value={this.state.value}
          onChange={this.handleChange}
          ref="name"
        />
        {/* requirement: the following 2 lines cannot be modified */}
        <button onClick={e => { this.handleClick("Steve"); }}>Steve</button>
        <button onClick={e => { this.handleClick("Emily"); }}>Emily</button>
      </div>
    );
  }
}

Any idea on how to get this working?

Thanks!

Viewsonic
  • 827
  • 2
  • 15
  • 34
  • Your sandbox link is working, what you want to do actually? – Just code Dec 28 '18 at 05:29
  • when you click the buttons: `Steve` / `Emily` the name on the `ParentComponent` doesn't get updated – Viewsonic Dec 28 '18 at 05:31
  • Check this https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js – Praveen Rao Chavan.G Dec 28 '18 at 05:37
  • @Justcode the parent component don't get the text underneath updated when on the child component you click the buttons: `Steve` / `Emily`. I need the event gets passed to the parent component. – Viewsonic Dec 28 '18 at 05:43
  • @PraveenRaoChavan.G I tried the approach there but didn't work, here is my try: https://codesandbox.io/s/1q3mpkmrnl. Could you provide some fork of the code on codesandbox.io? – Viewsonic Dec 28 '18 at 05:44
  • @Viewsonic You are trying to get the `event.target` instead, you can work directly with the value. you can pass only the value to the function, so that it can be called from anywhere without passing the event, only the value. Is it feasible in your case? – Surya Purohit Dec 28 '18 at 06:48

4 Answers4

8

You are missing the track of the input change,

Because React tracks when you set the value property on an input to keep track of the node's value. When you dispatch a change event, it checks it's last value against the current value and if they're the same it does not call any event handlers (as no change has taken place as far as react is concerned). So we have to set the value in a way that React's value setter function will not be called, which is where the setNativeValue comes into play.

Here you are setting the state instead of changing the input's value directly so, it will not get the updated value when you are dispatching the event. and if you write value directly like input.value it can not track the changes of the input.

so, you should set the value and dispatch the event, this way you can have the updated value when event is dispatched.

Here is the link of the reference and another, there are other ways too, to fire the change event.

Here is the function you need to set the property so that react can track the changes,

  handleClick = name => {
    const inputName = this.refs.name;
    console.log("Name before being changed: " + inputName.value); // this works

    var event = new Event("input", { bubbles: true });

    this.setNativeValue(inputName, name);
    inputName.dispatchEvent(event);
  };

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

Demo

Just code
  • 13,553
  • 10
  • 51
  • 93
0

You can include an onClick function to the parent component to change the state and pass it onto the child component. In the child component you can call the onClick method passed down via props and pass the name when button is clicked.

You can find the example code in the link provided below.

https://codesandbox.io/s/k0n1j2rrx7

Muljayan
  • 3,588
  • 10
  • 30
  • 54
0

I corrected your code like that: in handleChange function of parentComponent I changed the parameter to value from event. In childComponent I added the function call below to handleClick function:

 this.props.onChange(name);

Why I did these changes because, you call your parentComponent's handleChange function from your childComponent' s onChange function. When you click the button, it calls handleClick function. In handleClick function it calls property onChange function. The props.onChange function calls handleChange function of parent component. handleClick -> this.props.onChange(name) -> this.handleChange(name) I hope it helps.

mstfyldz
  • 482
  • 1
  • 6
  • 12
-1

If I understood correctly, you are trying to change the parent text like Hello Peter when you click the button from child component.

Check my codesandbox : codesandbox

Changes:

In child component, pass a props like this.props.onClick(name);

In parent, get it like

  handleClick = name => {
    this.setState({
      name
    });
  };

Hope this is will help you.

Sangeeth
  • 335
  • 3
  • 13
Jayavel
  • 3,377
  • 2
  • 21
  • 35