0

I am creating a dynamic list for a menu in ReactJs. The data for each menu item is bing pulled in from a JSON file via AJAX. I want the menu to close when a menu item is clicked. I am giving the whole list the prop "whenClicked" when one of the menu items is clicked. Below is the code:

var MenuList = React.createClass({
    getInitialState: function() {
        return {data: []}
    },
    componentWillMount: function() {
        $.ajax({
            url: 'http://10.0.0.97:8888/public-code/data/data.json',
            dataType: 'json',
            success: function(data) {
                this.setState({data: data});
            }.bind(this),
            error: function(xhr, status, error) {
                var err = JSON.parse(xhr.responseText);
                console.log(err.Message);
            }
        });
    },
    render: function() {
        var list = this.state.data.map(function(menuItemProps) {
            return <MenuItem onClick={this.props.whenClicked} {...menuItemProps} key={menuItemProps.id} />
        });
        return (
            <ul id="menu-list">
                {list}
            </ul>
        )
    }
});

The "whenClicked" prop then triggers a function in the parent menu called "handleClick" which changes the state of the menu and closes it if it is open. Here is the code for the parent menu component which contains the MenuList component above:

module.exports = React.createClass({

    getInitialState: function() {
        return {open: false, mobi: false}
    },
    handleClick: function() {
        this.setState({open: !this.state.open})
    },
    closeOnMobiScroll: function() {
        /*
        if(this.state.mobi === false) {
            this.setState({open: false})
        }
        */
    },
    updateDimensions: function() {
        $(window).width() >= 767 ? this.setState({mobi: true}) : this.setState({mobi: false});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        $(window).on("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        $(window).on("resize", this.updateDimensions);
    },
    render: function() {
        return (

            <div id="menu" className={(this.state.open ? 'open' : '')} >
                <div id="menu-inner-wrap">
                    <MenuTitle />
                    <MenuToggle whenClicked={this.handleClick}/>
                    <MenuList whenClicked={this.handleClick}/>
                </div>
            </div>

        )
    }

});

The problem is that the entire script breaks before the AJAX call fires and I get the following error:

Uncaught TypeError: Cannot read property 'whenClicked' of undefined

Since I do not see my AJAX "fail" message in the console I suspect the problem is that React is trying to wire

onClick={this.props.whenClicked}

before my data is loaded.

Is there a way to solve this? Is there something else I am missing?

HelloWorld
  • 2,480
  • 3
  • 28
  • 45

2 Answers2

1

The problem has nothing to do with the Ajax call or with React.

Inside the callback you pass to .map, this refers to the global object, not the component. The global object doesn't have a prop property, hence the error.

You can use .bind to bind the this value of the function to the component:

this.state.data.map(function(...) { ... }.bind(this));

More info: How to access the correct `this` context inside a callback?

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

Looks like you need to bind the proper context for the mapping function. Try:

var list = this.state.data.map( function(menuItemProps) {
    return <MenuItem onClick={this.props.whenClicked} {...menuItemProps} key={menuItemProps.id} />
}.bind(this) );
Gaurav
  • 562
  • 3
  • 12