最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - reactjs - Uncaught TypeError: Cannot read property 'map' of undefined - Stack Overflow

programmeradmin0浏览0评论

I'm trying to use the contents of a json return to not only populate my list, but when creating a new list to allow the selection of a parent.

I have the list ing up in my Navigation but when I click on "Create New List" it gives the following error:

Uncaught TypeError: Cannot read property 'map' of undefined

Which occurs on this particular part of code:

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
        )
    );

Code:

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(ment) {
    // 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>
      );
  },
  ponentDidMount: 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
}

I'm trying to use the contents of a json return to not only populate my list, but when creating a new list to allow the selection of a parent.

I have the list ing up in my Navigation but when I click on "Create New List" it gives the following error:

Uncaught TypeError: Cannot read property 'map' of undefined

Which occurs on this particular part of code:

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
        )
    );

Code:

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(ment) {
    // 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>
      );
  },
  ponentDidMount: 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
}
Share edited Jan 30, 2016 at 22:20 whoisearth asked Jan 30, 2016 at 22:18 whoisearthwhoisearth 4,17017 gold badges70 silver badges135 bronze badges 2
  • 1 So this.props.data clearly isn't an array, did you try logging it to see what it really is – adeneo Commented Jan 30, 2016 at 22:20
  • 1 Try to log the render call, it may be called several times, the first before the props are ready, to this.props.data may be undefined by this moment, which would cause this error. I'd also remend using lodash's _.map which is type-safe. – rishat Commented Jan 30, 2016 at 22:21
Add a ment  | 

2 Answers 2

Reset to default 4

You need a return value in getDefaultProps.

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

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

Also, it's good practice to define your prop types too:

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

Whilst I'd avoid ES6/7 Class sugar as I'm not a fan of that specification (it's unhealthy for your JS mind like sugar), nevertheless, if you like it then their is also a way to define these as static property 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

this.props.data is not defined the first time render() is first called, and you are trying to call .map() on undefined, which results in an error.

You should check that data is defined before calling map on it, and otherwise set navNodes to an empty array.

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

评论列表(0)

  1. 暂无评论