Reputation:
I simply want to render li to ul(an ordered list) from an array but for some reason it doesn't work for me. This shows an error Map is not a function
when I map through the array. Any help would greatly be appreciated
import React from 'react';
import './style.css';
class List extends React.Component {
constructor() {
super();
this.state = {
list: [<li>Default-li</li>]
}
}
render() {
return (
<div className='global'>
<button onClick={() => {
this.setState({ list: this.state.list.push(<li>Added-li</li>) })
console.log(this.state.list.length);
}
}>ADD</button>
<ul>
{
this.state.list.map((li) => {
return (li);
})
}
</ul>
</div>
);
}
}
export default List;
Upvotes: 1
Views: 71
Reputation: 2528
import React from 'react';
class List extends React.Component {
constructor(){
super();
this.state = {
list:["Default-li"]
}
}
render() {
return(
<div className = 'global'>
<button onClick = { () => {
this.state.list = Object.assign([],this.state.list,this.state.list.push("a"))
{console.log(this.state.list)}
this.forceUpdate()
}
}>ADD</button>
<ul>
{
this.state.list.map((li) =>{
return(<li>{li}</li>)
})
}
</ul>
</div>
);
}
}
export default List;
verified. You can try it as the type of this.state object is always object so we just need to make it array hope this help it is working on my pc hope it work for you to.
Upvotes: 0
Reputation: 31024
You get it because in the first render
the list
is not populated yet.
Try to conditionally render it:
this.state.list && this.state.list.map((li) => {
return (li);
})
Another issue is with your setState
, it's adviced to use the functional version of setState and access the list
within the parameter of it (i'm also using ES6 spread syntax instead of pushing and mutating the array ):
this.setState(prevState => ({ list: [...prevState.list, <li>Added-li</li>] }))
Running example:
class List extends React.Component {
constructor() {
super();
this.state = {
list: [<li>Default-li</li>]
};
}
render() {
return (
<div className="global">
<button
onClick={() => {
this.setState(prevState => ({ list: [...prevState.list, <li>Added-li</li>] }))
console.log(this.state.list.length);
}}
>
ADD
</button>
<ul>
{this.state.list &&
this.state.list.map(li => {
return li;
})}
</ul>
</div>
);
}
}
ReactDOM.render(<List />, document.getElementById("root"));
<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>
Edit
As a followup to your comment,
The functional version of setState
was not the issue here but it is better to use because setState
batches the updates and you may not have the current updated state that you expect.
With the functional setState
version, you guaranty to get the current "version" of the state.
You can read more about it in the DOCS.
The real issue here was that you used Array#push
which is not returning the array but the new length of it (it will mutate the original array which is considered as bad practice as well).
This is why i used the ES6 array spread syntax which allow us to create a shallow copy for a new array and combine it with a new value.
Upvotes: 1
Reputation: 821
Read the docs... the Array.prototype.push() of JavaScript does not return a new array but the length of the new array... I'll suggest you change your function to be
<button onClick={() => {
let list = this.state.list
list.push(<li>Added-li</li>)
this.setState({ list })
console.log(this.state.list.length);
}
}>ADD</button>
Upvotes: 0