user9345978
user9345978

Reputation:

Doesn't return <li> from array

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

Answers (3)

Rajan Lagah
Rajan Lagah

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

Sagiv b.g
Sagiv b.g

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

Daniel Adepoju
Daniel Adepoju

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

Related Questions