Reputation: 811
For some reason it sometimes maps through, but I get another error of not returning anything and other times it just says can't read property map of undefined. I'm trying to compile a list of users in React.
I have a component called UserList that is querying my database for all users and updating the state:
const UserList = React.createClass({
getInitialState: function () {
return {
users: []
}
},
componentDidMount: function () {
this.loadUsersFromServer();
},
loadUsersFromServer: function () {
axios.get('/api/users').then((users) => {
this.setState({users: users.data.users});
});
},
render: function () {
return (
<div>
<h1>User List</h1>
<User
users={this.state.users}
/>
</div>
);
},
});
I'm then passing it to it's child component User, and that is where the error is coming into play:
const User = React.createClass({
render: function () {
console.log('props: ' + JSON.stringify(this.props.users));
const users = this.props.users.map((user) => {
return (
<User
key={user._id}
username={user.username}
/>
);
});
return (
<div id="users">
{users}
</div>
);
},
});
What is interesting in the Chrome Dev tools is that for some reason I get three logs when trying to print out the this.props.users, and I'm not sure why it logs out three, but the middle one has all the users I'm looking for:
Any help would be greatly appreciated!
Upvotes: 3
Views: 13017
Reputation: 46341
At some moment (second refresh) this.props.users
looks undefined and the map function cannot handle it.
I rewrote the example using React.Component
syntax since I never worked with the old React (before V0.13) syntax.
Check more on different syntax here.
However, this part:
const User = React.createClass({
render: function () {
console.log('props: ' + JSON.stringify(this.props.users));
const users = this.props.users.map((user) => {
return (
<User
key={user._id}
username={user.username}
/>
looks strange. I think you used User inside User.
Here is how I rewrote it, with some minor modification since I haven't user axios.
class UserList extends React.Component{
constructor(props){
super(props);
this.state = { users : [] };
}
componentDidMount() {
this.loadUsersFromServer();
}
loadUsersFromServer(){
this.setState({users:
[{
"_id": 1,
"username": "Nicole",
"password": "0uTjaH",
"_v": "7.6.9"
}, {
"_id": 2,
"username": "Steve",
"password": "22l8h6a8NPU",
"_v": "7.41"
}, {
"_id": 3,
"username": "Joyce",
"password": "yA3efEc",
"_v": "6.3"
}, {
"_id": 4,
"username": "Todd",
"password": "7LsQ9aHqIfhy",
"_v": "7.1"
}, {
"_id": 5,
"username": "Maria",
"password": "uHZ5fkCi04v",
"_v": "9.31"
}]
});
}
render(){
return (
<div>
<h1>User List </h1>
<User users={this.state.users} />
</div>
);
}
};
class User extends React.Component{
constructor(props){
super(props);
}
render(){
console.log(typeof(this.props.users));
//console.log(this.props.users);
//const user = JSON.stringify(this.props.users);
const userList = this.props.users.map((user) => {
return (
<div>
<span>key={user._id}</span>
<span>username={user.username}</span>
</div>
);
});
return (
<div id="users">
{userList}
</div>
);
}
}
ReactDOM.render(<UserList /> , document.getElementById('root') );
span {padding: 3px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Upvotes: 0
Reputation: 463
You could try putting the users rendering logic in a method of your UserList class. Something close to this should work.
const UserList = React.createClass({
getInitialState: function () {
return {
users: []
}
},
componentDidMount: function () {
this.loadUsersFromServer();
},
loadUsersFromServer: function () {
axios.get('/api/users').then((users) => {
this.setState({users: users.data.users});
});
},
renderUsers: function (users) {
return <div>{users.map(user => <div key={user._id} username={user.username} />)}</div>
},
render: function () {
return (
<div>
<h1>User List</h1>
{ this.renderUsers(this.state.users) }
</div>
);
},
});
Upvotes: 1