我正在尝试使用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
}
答案 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...) : [];