Optiq
Optiq

Reputation: 3182

React: Function in Parent Component not receiving data from Child Component

I'm working on a project using React where I need to update the state of the Parent Component with the Child Component's input. I did console.log() my way through each function in the chain and found that the fetchText() function in my parent component didn't receive the text

Here's what my parent component looks like

class AppComponent extends React.Component{
  constructor(props){
    super(props);
    this.state = { markdownText: `` };

    this.fetchText = this.fetchText.bind(this);
  }

  fetchText(text){
    this.setState({ markdownText: text });
    console.log(text);
  }

  render(){
    return(
      <div id="app-grid">
        <h1>Markdown Viewer</h1>
        <MarkDownComponent userInput={this.fetchText} />
        <PreviewComponent />
      </div>
    );
  }
}

My Child Component looks like this

class MarkDownComponent extends React.Component{
  constructor(props){
    super(props);
    this.state = { text: ``};

    this.getInput = this.getInput.bind(this);
    this.sendInput = this.sendInput.bind(this);
  }

  getInput(event){
    this.setState({ text: event.target.value });
    this.sendInput();
  }

  sendInput(){
    this.props.userInput = this.state.text;
    //console.log(this.props.userInput);
  }

  render(){
    return(
      <div id="markdown-component">
        <textarea id="editor" rows="16" onChange={this.getInput}></textarea>
      </div>
    );
  }
}

When console.log()ing this.props.userInput in the Child Component I get the value back as I type. So that indicates the value is making it to the property, but why isn't it updating in the parent component?

Upvotes: 0

Views: 160

Answers (3)

devserkan
devserkan

Reputation: 17608

As told in the comments, there is no need to assign a state to your function. Also, if your desire is to change the text with a Child and nothing more you don't need a state in your Child. Don't use state if it is not necessary.

class AppComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { markdownText: "" };

    this.fetchText = this.fetchText.bind(this);
  }

  fetchText(e) {
    this.setState({ markdownText: e.target.value});
  }

  render() {
    return (
      <div id="app-grid">
        <h1>Markdown Viewer</h1>
        Value is now: {this.state.markdownText}
        <MarkDownComponent userInput={this.fetchText} />
      </div>
    );
  }
}

const MarkDownComponent = ( props ) => {
  return (
    <div id="markdown-component">
      <textarea id="editor" rows="16" onChange={props.userInput}></textarea>
    </div>
  )
}

ReactDOM.render(<AppComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Upvotes: 1

McRist
McRist

Reputation: 1748

Few things to note here:

you cannot change the value of props, it is passed to the component through it parent

this.props.userInput = this.state.text;

this won't work.

So, to make fetchData of parent get the text from textarea you should do like this

<textarea id="editor" rows="16" onChange={this.props.userInput}></textarea>

and in parent component :

fetchText(event){
    console.log(event.target.value)
    this.setState({ markdownText: event.target.value });
}

you don't require functions like getInput and sendInput to send data to the parent component.

Upvotes: 2

Hemadri Dasari
Hemadri Dasari

Reputation: 33974

The issue is you are assigning state value to a function which is not correct.

this.props.userInput = this.state.text; // this is incorrect

//Right one

class MarkDownComponent extends React.Component{
  constructor(props){
    super(props);
    this.state = { text: ``};

    this.getInput = this.getInput.bind(this);
    this.sendInput = this.sendInput.bind(this);
  }

  getInput(event){
    this.setState({ text: event.target.value });
    this.sendInput();
  }

  sendInput(){
    this.props.userInput(this.state.text);
    //console.log(this.props.userInput);
  }

  render(){
    return(
      <div id="markdown-component">
        <textarea id="editor" rows="16" onChange={this.getInput}></textarea>
      </div>
    );
  }
}

You can directly call this.props.userInput function in getInput function:

class MarkDownComponent extends React.Component{
      constructor(props){
        super(props);
        this.state = { text: ``};

        this.getInput = this.getInput.bind(this);
      }

      getInput(event){
        this.props.userInput(event.target.value);
      }

      render(){
        return(
          <div id="markdown-component">
            <textarea id="editor" rows="16" onChange={this.getInput}></textarea>
          </div>
        );
      }
    }

ES6 way:

class MarkDownComponent extends React.Component{
      constructor(props){
        super(props);
        this.state = { text: ``};
      }

      getInput = (event) => {
        this.props.userInput(this.state.text);
      }

      render(){
        return(
          <div id="markdown-component">
            <textarea id="editor" rows="16" onChange={this.getInput}></textarea>
          </div>
        );
      }
    }

Upvotes: 2

Related Questions