1

I get the following error when mapping the state for my posts:[] object

TypeError: this.state.posts.map is not a function

I looked at something similar but it didn't come close to resolving the problem.

The console.log() shows the current data, so the function works, it just a matter of passing the data within the posts:[] object and mapping it.

{this.state.posts.map((post, key)=> {
    return(
        <div key={key}>
            <h2>{post.description}</h2>

            <small>{post.username}</small>

        </div>
    ); 
})}

Dashboard.js (updated)

class Dashboard extends Component{

    constructor(props){
        super(props)

        this.state = {
            username:"",
            loading: true,
            posts:{},
            myArray:[]
        }

    }
    componentWillMount(){
        this.getPosts()
    }

    getPosts() {
        const collection2 = fire.collection('posts');
        collection2.get().then(snapshot => {
            snapshot.forEach(doc => { 
                let items = doc.data();
                // items = JSON.stringify(items);
                this.setState({
                  posts: items
                });
            })    
        const eArray = Object.values(this.state.posts);
        this.setState({
            myArray:[...eArray]
        })

        console.log(this.state.myArray)
        })


    }


    componentDidMount(){
        if(this.props.userId){
            const collection = fire.collection('users');
            collection.get().then(snapshot => {     
              snapshot.forEach(doc => { 
                this.setState({
                    username: doc.data().username,
                    loading:false
                })                 
              });   
            });

        }

    }

    render(){
        if (!this.props.userId) return <Redirect to='/' />
        const { loading, posts,myArray } = this.state;

        if(loading){
           return(
            <div className="loader"></div>
           ) 
        }
        return(
            <div className="container"> 
                <div className="row">
                    <div className="col-md-6 mt-3">
                        <h1>Welcome {this.state.username.toLowerCase()}</h1>

                        {myArray.map((post, key)=> {
                            return(
                                <div key={key}>
                                    <h2>{post.description}</h2>

                                    <small>{post.username}</small>


                                </div>
                            ); 
                        })}
                    </div>
                </div>
            </div>


        );
    }


}
Jasmine
  • 35
  • 4
  • when you `console.log(this.state.posts)` is it an array? – Edgar Henriquez Jan 10 '19 at 21:19
  • @EdgarHenriquez `{ }` with data about the post information, it's in object not array – Jasmine Jan 10 '19 at 22:25
  • You can't use `.map` on an Object. [More info about Array.prototype.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) – Edgar Henriquez Jan 11 '19 at 00:55
  • i sorta figured it out, the code is a little bit better, but it just a matter of get the elements to show. I'll update the code so u can see what i have now. – Jasmine Jan 11 '19 at 01:01
  • it looks somewhat hacky imo, i pass the objects within an array now. I see a console.log with data but there is an issue that is not allowing it to show it on the page. – Jasmine Jan 11 '19 at 01:04
  • You shouldn't call `this.setState` in a loop. Instead, save the data an `someArray` and push `someArray.push(doc.data())` each item. Then call `this.setState` once after the loop. – Edgar Henriquez Jan 11 '19 at 01:08
  • 1
    can u show an example with the existing code, im kinda confused. – Jasmine Jan 11 '19 at 01:09
  • this is what i have now `collection2.get().then(snapshot => { this.setState({ posts:snapshot.data() }) })` – Jasmine Jan 11 '19 at 01:10
  • just getting `snapshot.data is not a function` – Jasmine Jan 11 '19 at 01:10

2 Answers2

2

The error that you're getting in the console is because you're using .map on an Object, but map is a method of Array. More info on .map

There's also another problem with your code and is that you shouldn't call this.setState inside a loop. Instead, collect the data you need from the loop and then update the state.

Something like this


      getPosts() {
        const collection2 = fire.collection('posts');
        collection2.get().then(snapshot => {
            let arr = [];
            snapshot.forEach(doc => { 
              arr.push(doc.data()); //Collect all the items and push it into the array
            })
            this.setState({
              posts: arr,
            })
        })
    }

I don't know exactly what you're trying to do on componentDidMount. But this should point you in the right direction and solve the error that you're experiencing with map not being a function.

Edgar Henriquez
  • 1,373
  • 1
  • 14
  • 17
0

You setting state in forEach loop. You just set last element of collection2.get() result to state.posts which makes forEach loop meaningless. There is no difference

   collection2.get().then(snapshot => {
        snapshot.forEach(doc => {
            this.setState({
                posts: doc.data()
            })
        })
        console.log(this.state.posts)
    })

with

   collection2.get().then(snapshot => {
            this.setState({
                posts: snapshot[snapshot.length - 1].doc.data()
            })
    })

setState asynchronous and will conflict with the different states available on each iteration in that forEach () loop.

aseferov
  • 6,155
  • 3
  • 17
  • 24
  • i get `Unhandled Rejection (TypeError): Cannot read property 'doc' of undefined` – Jasmine Jan 10 '19 at 22:24
  • im trying some variations when removing the doc `Unhandled Rejection (TypeError): Cannot read property 'data' of undefined` – Jasmine Jan 10 '19 at 22:31