1

http://jsfiddle.net/adamchenwei/3rt0930z/23/ I tried apply solution from here react.js call parent function from child

But I encounter issue: 3VM606:43Uncaught TypeError: Cannot read property 'changeName' of undefined

Anyone knows why this is happening and how to fix it so that the onClick will change the name on every item, instead of just the one that got clicked on?

var items = [
    { name: 'Believe In Allah', link: 'https://www.quran.com' },
    { name: 'Prayer', link: 'https://www.quran.com' },
    { name: 'Zakat', link: 'https://www.quran.com' },
    { name: 'Fasting', link: 'https://www.quran.com' },
  { name: 'Hajj', link: 'https://www.quran.com' },
];

var ItemModule = React.createClass({
    getInitialState: function() {
        return { newName: this.props.name }
    },
    changeItsName() {
    this.props.changeTheName();
  },
    render() {
    //<!-- <a className='button' href={this.props.link}>{this.props.name}</a> -->
    return (
        <li onClick={this.changeItsName}>

        {this.state.newName}
      </li>
    );
  }
});

var RepeatModule = React.createClass({
    getInitialState: function() {
        return { items: [] }
    },
  changeName() {
    console.log('changed name');
    this.setState({ newName: 'Test' });
  },
    render: function() {

        var listItems = this.props.items.map(function(item) {
            return (
                <div>
          <ItemModule
            name={item.name}
            changeTheName={this.changeName.bind(this)}/>
        </div>
            );
        });

        return (
            <div className='pure-menu'>
                <h3>Islam Pillars</h3>
                <ul>
                    {listItems}
                </ul>
            </div>
        );
    }
});

ReactDOM.render(<RepeatModule items={items} />,                 
    document.getElementById('react-content'));

Thanks!

Community
  • 1
  • 1
ey dee ey em
  • 7,991
  • 14
  • 65
  • 121

2 Answers2

1

you have a scoping issue, within the map function this is referencing the map function not your react object. Either use es6 arrow syntax if you are using a transpiler or do the following

    var self = this;
    var listItems = this.props.items.map(function(item) {
        return (
            <div>
      <ItemModule
        name={item.name}
        changeTheName={self.changeName}/>
    </div>
        );
    });

The ES6 way of doing it would be:

var listItems = this.props.items.map((item) => 
    <div>
      <ItemModule
        name={item.name}
        changeTheName={this.changeName}/>
       </div>
    );

There is no need to do the this.changeName.bind(this) either, it actually makes react less performant when you do bindings within a components prop. see: https://daveceddia.com/avoid-bind-when-passing-props/https://daveceddia.com/avoid-bind-when-passing-props/

finalfreq
  • 6,830
  • 2
  • 27
  • 28
  • I updated fiddle. But none of the item are changing the name now... thought? – ey dee ey em Sep 20 '16 at 21:30
  • Since you are managing the state of name within the ItemModule component instead of passing it down as props you need to change your `changeItsName() { this.props.changeTheName(); }` to become `changeItsName() { this.setState({ newName: 'test' }) } ` If you wanted RepeatModule to control it, you'd need to have some sort of id for each item in the array, pass the id into the `this.props.changeTheName` function and then use that to find the item in the array and change its name there – finalfreq Sep 20 '16 at 21:53
  • http://jsfiddle.net/pLbkzb16/1/ here is a fiddle of it working. note i removed state from the item and the repeatmodule is handling all state now and i also added ids to your items so they can be easily looked up when the name needed to be changed – finalfreq Sep 20 '16 at 22:06
  • 1
    It works now, but it still does not change all items at once though, its change the value only when I click on that item. The effect I try to achieve is if I click on one of them, all the names change to 'lalala'. Thought? – ey dee ey em Sep 20 '16 at 22:37
  • instead of finding the specific item to change, just map over the array of items and set each name to 'Test' and then set state to the mapped over array `changeName(id) { var items = this.state.items.map(function(item) { item.name = 'Test' return item }); this.setState({items: items}) },` – finalfreq Sep 20 '16 at 22:43
1

did you mean this -> http://codepen.io/dagman/pen/JRbyLj

const items = [{
    name: 'Believe In Allah',
    link: 'https://www.quran.com'
}, {
    name: 'Prayer',
    link: 'https://www.quran.com'
}, {
    name: 'Zakat',
    link: 'https://www.quran.com'
}, {
    name: 'Fasting',
    link: 'https://www.quran.com'
}, {
    name: 'Hajj',
    link: 'https://www.quran.com'
}, ];

const ItemModule = ({
    name,
    changeName
}) => (
    <li onClick={changeName}>
        {name}
  </li>
);

const RepeatModule = React.createClass({
    getInitialState: function() {
        return {
            newName: ''
        }
    },
    changeName() {
        console.log('changed name');
        this.setState({
            newName: 'Test'
        });
    },
    render() {
        const listItems = this.props.items.map((item) => {
            return (
                <ItemModule
                  name={this.state.newName || item.name}
                  changeName={this.changeName.bind(this)}
                />
      );
        });

        return (
            <div className='pure-menu'>
                <h3>Islam Pillars</h3>
                <ul>
                    {listItems}
                </ul>
            </div>
        );
    }
});

ReactDOM.render(
    <RepeatModule items={items} />,
    document.getElementById('react-content')
):
devellopah
  • 471
  • 2
  • 15