reactjs - 未捕获的TypeError:无法读取未定义的属性“map”

时间:2016-01-30 22:18:47

标签: javascript reactjs

我正在尝试使用json返回的内容,不仅填充我的列表,而且在创建新列表时允许选择父级。

我的导航列表出现了,但是当我点击“创建新列表”时,它会出现以下错误:

Uncaught TypeError: Cannot read property 'map' of undefined

在代码的这一特定部分发生了这种情况:

render: function() {
  var navNodes = this.props.data.map(function(nav) {
    return (
      React.createElement(NavItems, {name: nav.name, key: nav.id})
    );
  });
    return (
        React.createElement(Input, {
          label: "Parent List", 
          ref: "input", 
          value: this.state.value, 
          bsStyle: this.state.validationState, 
          hasFeedback: true, 
          help: this.state.hint, 
          onChange: this.handleChange, 
          type: "select"}, 
            this.renderPlaceholder(), 
            navNodes
        )
    );

代码:

var Bootstrap = ReactBootstrap;
var Input = ReactBootstrap.Input;

var NavItems = React.createClass({
  render: function() {
    return (
      <option value={1}>{navNodes}</option>
    );
  }
});

var NavItem = React.createClass({
  render: function() {
    return (
      <li><a href="#">{this.props.name}</a></li>
    );
  }
});

var NavList = React.createClass({
  render: function() {
    var navNodes = this.props.data.map(function(nav) {
      return (
        <NavItem name={nav.name} key={nav.id}></NavItem>
      );
    });
    return (
      <ul className="nav">
        <li className="current"><a href="#"><i className="glyphicon glyphicon-home"></i> Lists</a></li>
        {navNodes}
      </ul>
    );
  }
});

var NewListButton = React.createClass({
  render: function() {
    return (
      <a {...this.props}
        href="javascript:;"
        role="button"
        className={(this.props.className || '') + ' btn'}
      />
    );
  }
});

var ListNameInput = React.createClass({
    getInitialState: function() {
        return {
            value: '',
            hint: null,
            validationState: null
        };
    },
    handleChange: function() {
        var newValue = this.refs.input.getValue(),
            hint = null,
            validationState = null,
            length = newValue.length;
        if (length > 2) { 
            validationState = 'success'; 
        } else {
            validationState = 'error'; 
            if (length) {
                hint = 'The name must be at least 3 characters long.';
            } else {
                hint = 'This value is required.';
            }
        }
        this.setState({
            value: newValue,
            hint: hint,
            validationState: validationState
        }); 
    },
    isValid: function() {
        return ('success' === this.state.validationState);
    },
    render: function() {
        return (
            <Input
              label='Name'
              ref='input'
              value={this.state.value}
              placeholder='Enter List Name'
              bsStyle={this.state.validationState}
              hasFeedback
              help={this.state.hint}
              onChange={this.handleChange}
              type='text' />
        );
    }
});

var ListDescriptionInput = React.createClass({
    getInitialState: function() {
        return {
            value: '',
            hint: null,
            validationState: null
        };
    },
    handleChange: function() {
        var newValue = this.refs.input.getValue(),
            hint = null,
            validationState = null,
            length = newValue.length;
        if (length > 2) { 
            validationState = 'success'; 
        } else {
            validationState = 'error'; 
            if (length) {
                hint = 'The description must be at least 3 characters long.';
            } else {
                hint = 'This value is required.';
            }
        }
        this.setState({
            value: newValue,
            hint: hint,
            validationState: validationState
        }); 
    },
    isValid: function() {
        return ('success' === this.state.validationState);
    },
    render: function() {
        return (
            <Input
              label='Description'
              ref='input'
              value={this.state.value}
              placeholder='Enter Description'
              bsStyle={this.state.validationState}
              hasFeedback
              help={this.state.hint}
              onChange={this.handleChange}
              type='text' />
        );
    }
});

var WidgetListSelect = React.createClass({
    getInitialState: function() {
        return {
            value: 0,
            hint: null,
            validationState: null
        };
    },
    handleChange: function() {
        var newValue = Number(this.refs.input.getValue()),
            hint = null,
            validationState = null;
        if (0 === newValue) {
            validationState = 'error';
            hint = 'You must select an parent list.';
        } else {
            validationState = 'success';
        }
        this.setState({
            value: newValue,
            hint: hint,
            validationState: validationState
        });
    },
    isValid: function() {
        return ('success' === this.state.validationState);
    },
    renderPlaceholder: function() {
        if (0 !== this.state.value)
            return null;
        // Show placeholder only prior to a value being selected
        return (
            <option value={0}>
                Please select a Parent List
            </option>
        );
    },
    render: function() {
      var navNodes = this.props.data.map(function(nav) {
        return (
          <NavItems name={nav.name} key={nav.id}></NavItems>
        );
      });
        return (
            <Input
              label='Parent List'
              ref='input'
              value={this.state.value}
              bsStyle={this.state.validationState}
              hasFeedback
              help={this.state.hint}
              onChange={this.handleChange}
              type='select'>
                {this.renderPlaceholder()}
                {navNodes}
            </Input>
        );
    }
});

var CreateNewListForm = React.createClass({
    handleSubmit: function() {
        var isValid = 
            !!(this.refs.nameInput.isValid()
                & this.refs.descriptionInput.isValid() 
                & this.refs.widgetlistSelect.isValid());
        if (isValid) {
            this.props.closeModal();
        } else {
            // Force validation of each element to show errors to user
            this.refs.nameInput.handleChange();
            this.refs.descriptionInput.handleChange();
            this.refs.widgetlistSelect.handleChange();
        }
    },
    render: function() {
        return (
            <div>
                <ListNameInput 
                  ref='nameInput' />
                <ListDescriptionInput 
                  ref='descriptionInput' />
                <WidgetListSelect 
                  ref='widgetlistSelect' />
                <Bootstrap.ButtonToolbar>
                    <Bootstrap.Button
                      onClick={this.handleSubmit}>
                        Save
                    </Bootstrap.Button>
                    <Bootstrap.Button
                      onClick={this.props.closeModal}>
                        Cancel
                    </Bootstrap.Button>
                </Bootstrap.ButtonToolbar>
            </div>
        );
    }
});

var NavBox= React.createClass({
  loadNavsFromServer: function() {
    $.ajax({
      url: "http://servername/api/widgetlists/?format=json",
      dataType: 'json',
      cache: false,
      success: function(data) {
        this.setState({data: data});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error("http://servername/api/widgetlists/?format=json", status, err.toString());
      }.bind(this)
    });
  },
  handleListSubmit: function(comment) {
    // TODO: submit to the server and refresh the list
  },
  getInitialState: function() {
    return {
      modalVisible: false,
      data: []};
  },
  onClick: function() {
      this.setState({modalVisible: true});
  },
  hideModal: function() {
      this.setState({modalVisible: false});
  },
  renderModal: function() {
      return (
          <Bootstrap.Modal 
            show={this.state.modalVisible}
            onHide={this.hideModal}>
              <Bootstrap.Modal.Body>
                  <CreateNewListForm 
                    closeModal={this.hideModal} />
              </Bootstrap.Modal.Body>
          </Bootstrap.Modal>
      );
  },
  componentDidMount: function() {
    this.loadNavsFromServer();
  },
  render: function() {
    return (
      <div className="col-md-2">
        <div className="sidebar content-box" style={{display: "block"}}>
            <NavList data={this.state.data} />
            {this.renderModal()}
            <Bootstrap.Button onClick={this.onClick}>
                Create New List
            </Bootstrap.Button>
         </div>
      </div>
    );
  }
});

module.exports = {
    NavBox: NavBox
}

2 个答案:

答案 0 :(得分:4)

您需要getDefaultProps中的返回值。

https://facebook.github.io/react/docs/reusable-components.html#default-prop-values

getDefaultProps: function() {
    return {
      data: []
    };
  }

此外,定义您的道具类型也是一种很好的做法:

propTypes: {
  data: React.PropTypes.array.isRequired
}

虽然我不会使用ES6 / 7级糖,因为我不是那种规格的粉丝(这对你的JS思维来说不像糖一样),但是,如果你喜欢它,那么他们的也可以将这些定义为static属性defaultProps

class Foo extends React.Component {
  static defaultProps = {
    bar: React.PropTypes.array.isRequired
  }
}

https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#es7-property-initializers

答案 1 :(得分:1)

第一次调用this.props.data时未定义

render(),并且您尝试在未定义时调用.map(),这会导致错误。

您应该在调用数据之前检查数据是否已定义,否则将navNodes设置为空数组。

    const data = this.props.data;
    const navNodes = data ? data.map(...your function...) : [];