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, tothis.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
2 Answers
Reset to default 4You 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...) : [];