Aakash
Aakash

Reputation: 83

Unable to bind handler in React

I am trying to bind a method of a parent component to the state of its child component but I'm unable to get the desired result. I checked the value of 'this' in App component and it still points to the App component. Should it not be pointing to the ItemsList component since its being binded to it using bind()? Can someone please point out the mistake I'm making.

import React from 'react';
import {render} from 'react-dom';

class Item extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return <div> {this.props.value} </div>;
  }
}

class ItemList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      itemArray: ['Work', 'Learn React']
    }
    this.props.adder.bind(this);
    console.log(this.props.adder)
  }

  render() {
    const items = this.state.itemArray.map(el=><Item key={el} value={el} />);
    return (
      <div> 
        <h2> To Do List </h2>
        <ul>{items}</ul>
      </div>
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  addElement (data) {
    let items = this.state.ItemList;
    items.push(<Item value={data} />);

  }

  render() {
    return (
      <div>
        <input type="text" ref={input=>this.input=input} />
        <input type="button" value="Add" onClick={()=>this.addElement(this.input.value)}/>
        <ItemList adder={this.addElement} />
      </div>
    );
  }
}


render(<App />, document.getElementById('root'));

Upvotes: 0

Views: 52

Answers (2)

Hum4n01d
Hum4n01d

Reputation: 1370

Though what you want is technically possible, this is a much more explicit easy to understand way to do it.

I re-factored your code so that the data flow only goes in one direction, from App to `Itemimport React from "react"; import { render } from "react-dom";

I also changed Item and ItemList to stateless components that take value and items as props respectively.

The main change is that App holds the state instead of ItemList

const Item = ({ value }) => <div>{value}</div>;

const ItemList = ({ items }) => (
  <div>
    <h2>To Do List</h2>
    {items.map(item => <Item key={item} value={item} />)}
  </div>
);

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: ["Work", "Learn React"]
    };
  }
  addElement(value) {
    this.setState(state => ({
      items: [...state.items, value]
    }));
  }
  render() {
    return (
      <div>
        <input type="text" ref={input => (this.input = input)} />
        <input
          type="button"
          value="Add"
          onClick={() => this.addElement(this.input.value)}
        />
        <ItemList items={this.state.items} />
      </div>
    );
  }
}

render(<App />, document.querySelector("#root"));

Here is a CodeSandbox with your working app: https://codesandbox.io/s/4r4v0w5o94

Upvotes: 1

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15292

Should it not be pointing to the ItemsList component since its being binded to it using bind()?

Well,the step you following in not right one.

In App Component

You need to store the ItemList (child) component reference in App(parent) component.

<ItemList adder={this.addElement} bindChild =  {(ref)=>this.itemList = ref}/>

In ItemList component,

you need to call bindChild method when ItemList component mounted.

componentDidMount(){
  this.props.bindChild(this);
}

Now, in your App (parent) component, you have reference for ItemList (child) component in this.itemList property.

In App component, you can use this.itemList to update state of ItemList (child) component.

addElement(data) {
    let items = this.itemList.state.itemArray;
    console.log(items);
    const newItem = <Item value={data} />

    this.itemList.setState({ itemArray : [...items, newItem]})
  }

Please check complete example on codesandbox

Upvotes: 1

Related Questions