简体   繁体   中英

React ES6 parent child components state issue

I am new to react and trying to build a simple ToDo app based off of the react-starter-kit. I am using ES6 classes and unable to find a way to update parent state from a child component.

Here is the code:

import React, { PropTypes, Component } from 'react';
import withStyles from '../../decorators/withStyles';
import styles from './ToDoPage.less';


@withStyles(styles)
class ToDoPage extends Component {

  static contextTypes = {
    onSetTitle: PropTypes.func.isRequired
  };

  constructor() {
    super();
    this.state = {
      items: ['Item1', 'Item2'],
      value: ''
    };
  }

  updateValue(newValue) {
    //this.state is null here
    this.setState({
      value: newValue 
    });
  }

  clickHandler() {
    console.log('AddToDo state:', this.state)
    if (this.state.value && this.state.items) { //this.state is null here
      this.setState({
        items: this.state.items.push(this.state.value)
      });
    }
  }

  render() {
    let title = 'To Do List';
    this.context.onSetTitle(title);
    return (
      <div className="ToDoPage">
       <div className="ToDoPage-container">
        <h1>{title}</h1>
          <AddToDoTextBox handleUpdate={this.updateValue}/>
          <AddToDoButton handleClick={this.clickHandler}/>
          <div className = "todo-items">
           <br/>
           <div>({this.state.items.length}) items found.</div>
           <ToDoList items = {this.state.items}/>
          </div>
        </div>
      </div>
    );
  };
}

class ToDoList extends Component {
  static propTypes = {
    items: PropTypes.array.isRequired
  };

  render() {
    console.log(this.props.items);
    return (
            <ul>
            {this.props.items.map(function(item) {
             return <li key={item}>{item}</li>
            }) }
            </ul>); 
  };
};

class AddToDoButton extends Component {
  static propTypes: {
    clickHandler: React.PropTypes.func 
  }

  constructor() {
    super(); 
  }

  render() { 
    return (<button 
            className="btn btn-primary" 
            onClick={this.props.handleClick.bind(this)}>Add ToDo</button>); 
  };
}

class AddToDoTextBox extends Component {
  static propTypes: {
    handleUpdate: React.PropTypes.func
  }

  constructor() {
    super();
    this.state = {
      value: '' 
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.setState({value: e.target.value}); 
    this.props.handleUpdate.call(this, e.target.value);
  }

  render() {
    return <input type="text" placeholder="Enter todo item here" size="50" onChange={this.handleChange}/> 
  };
}
export default ToDoPage;

I want to access ToDoPage's state from updateValue() and clickHandler() functions but this.state is null as I binding to the child components they are being called from (ie AddToDoButton and AddToDoTextBox). How can I access/update ToDoPage's state from clickHandler() or updateValue()?

When you want to do change from Child to Parent you need to pass a function to the Child as prop. Something like (example with some ES7 syntax):

Parent component

import React, { Component } from 'react'

export default class Parent extends Component {
  constructor() {
    super();

    this.handleChange = this.handleChange.bind(this);
    this.state = {
      number: 1
    };
  }

  handleChange(num) {
    this.setState({
      number: num
    });
  }

  render() {
    return (
      <Child changeNumber={this.handleChange} />
    );
  }
}

Child component

import React, { PropTypes, Component } from 'react'

export default class Child extends Component {
  static propTypes = {
    changeNumber: PropTypes.func
  }

  constructor() {
    super();

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

  handleClick(e) {
    e.preventDefault();
    this.props.changeNumber(e.target.value);
  }

  render() {
    return (
      <button onClick={this.handleClick}> 3 </button>
    );
  }
}

You need to use .bind(this) at your handleUpdate and handleClick assignments in ToDoPage.render() :

      <AddToDoTextBox handleUpdate={this.updateValue.bind(this)}/>
      <AddToDoButton handleClick={this.clickHandler.bind(this)}/>

or use arrow functions

See https://facebook.github.io/react/docs/reusable-components.html#no-autobinding

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM