Home > Net >  Heroku ReactJS app. Error when using map function, cannot read properties of undefined
Heroku ReactJS app. Error when using map function, cannot read properties of undefined

Time:05-17

I'm trying to make a sport/tinder like app for a school project from a friend of mine. It came together well on my localhost, but for him it was a requirement to host it online. Not really a professional in hosting, but I was a bit familiar with Heroku. I used a client and a server side for my application, so I build the client side and put it into the server side folder. This server side is hosted on the Heroku page. But whenever I try to login, it won't work and I get this error message in my console.

TypeError: Cannot read properties of undefined (reading 'map')

I know there are a lot of other people with the same issue. I tried many solutions and I think something might be wrong with my code. I'm very new to ReactJS, that's why I used a tutorial for making this application. And I'm more like a Data Scientist then a Software Engineer, but I'm always eager to learn. That's why I took the oppurtunity to learn this new 'language'. So I might be wrong when it comes to the problem.

The error says it is caused by this line of code.

const matchedUserIds = user?.matches.map(({user_id}) => user_id).concat(userId)

This is the whole Dashboard file I used for making this page. I'm using a MongoDB for the storage of my users.

import TinderCard from 'react-tinder-card';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import ChatContainer from '../components/ChatContainer'
import axios from "axios";

const Dashboard = () => {
const [user, setUser] = useState(null)
const [genderedUsers, setGenderedUsers] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
const [lastDirection, setLastDirection] = useState()

const userId = cookies.UserId
const getUser = async () => {
    try {
        const response = await axios.get('https://[app].herokuapp.com/user', {
            params: {userId}
        })
        setUser(response.data)
    } catch (error) {
        console.log(error)
    }
}

const getGenderedUsers = async () => {
    try {
        const response = await axios.get('https://[app].herokuapp.com/gendered-users', {
            params: {gender: user?.gender_interest}
        })
        setGenderedUsers(response.data)
    } catch (error) {
        console.log(error)
    }
}

useEffect(() => {
    getUser()
}, [])

useEffect(() => {
    if (user) {
        getGenderedUsers()
    }
}, [user])

const updateMatches = async (matchedUserId) => {
    try {
        await axios.put('https://[app].herokuapp.com/addmatch', {
            userId,
            matchedUserId
        })
        getUser()
    } catch (error) {
        console.log(error)
    }
}

const swiped = (direction, swipedUserId) => {
    console.log(direction, swipedUserId)
    if (direction === 'right') {
        updateMatches(swipedUserId)
    }

    setLastDirection(direction)
}

const outOfFrame = (name) => {
    console.log(name   ' left the screen!')
}

const matchedUserIds = user?.matches.map(({user_id}) => user_id).concat(userId)

const filteredGenderedUsers = genderedUsers?.filter(
    genderedUser => !matchedUserIds.includes(genderedUser.user_id)
)

return (<>
    {user && <div className="dashboard">
        <ChatContainer user={user}/>
        <div className="swipe-container">
            <div className="card-container">

                {filteredGenderedUsers?.map((genderedUser) =>
                    <TinderCard
                        className='swipe'
                        key={genderedUser.user_id}
                        onSwipe={(dir) => swiped(dir, genderedUser.user_id)}
                        onCardLeftScreen={() => outOfFrame(genderedUser.first_name)}>
                        <div style={{backgroundImage: 'url('   genderedUser.url   ')'}} className='card'>
                            <h3>{'Name: '   genderedUser.first_name} <br/> {'Sport: '   genderedUser.about}</h3>
                        </div>
                    </TinderCard>)}
                <div className="swipe-info">
                    {lastDirection ? <p>You swiped {lastDirection}</p> : <p/>}
                </div>
            </div>
        </div>
    </div>}
</>)
} 
export default Dashboard

Any help is welcome. If you need more code examples, please reply ;)

EDIT

My index.js file from my server, GET request for my user

app.get('/user', async (req, res) => {
const client = new MongoClient(uri)
const userId = req.query.userId

try {
    await client.connect()
    const database = client.db('app-data')
    const users = database.collection('users')

    const query = {user_id: userId}
    const user = await users.findOne(query)
    res.send(user)
} finally {
    await client.close()
}
})

My index.js file from my server, GET request for my gendered-users

app.get('/gendered-users', async (req, res) => {
const client = new MongoClient(uri)
const gender = req.query.gender

try {
    await client.connect()
    const database = client.db('app-data')
    const users = database.collection('users')
    const query = {gender_identity: {$eq: gender}}
    const foundUsers = await users.find(query).toArray()

    res.send(foundUsers)
} finally {
    await client.close()
}
})

A picture from my MongoDB users (Dummy data)

CodePudding user response:

I'm still a react beginner myself and can't comment yet so to the answer section it is lol.

If I'm right, you are probably getting Cannot read properties of undefined (reading 'map') error because user is null when its first initialized and therefore not have map() yet. You probably didn't error on user?.matches because of optional chaining. The solution here is putting const matchedUserIds = user?.matches.map(({user_id}) => user_id).concat(userId) in a useEffect hook with user as dependency.

If that doesn't work, reply back

CodePudding user response:

By the default the user is null. And I already know that you have other methods that follow the same approach, and this might result the same error. Just make a simple check on the fields that are asynchronous. I think the quickest and easiest example would be:

import TinderCard from 'react-tinder-card';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import ChatContainer from '../components/ChatContainer'
import axios from "axios";

const Dashboard = () => {
const [user, setUser] = useState(null)
const [genderedUsers, setGenderedUsers] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
const [lastDirection, setLastDirection] = useState()

const userId = cookies.UserId
const getUser = async () => {
    try {
        const response = await axios.get('https://[app].herokuapp.com/user', {
            params: {userId}
        })
        setUser(response.data)
    } catch (error) {
        console.log(error)
    }
}

const getGenderedUsers = async () => {
    try {
        const response = await axios.get('https://[app].herokuapp.com/gendered-users', {
            params: {gender: user?.gender_interest}
        })
        setGenderedUsers(response.data)
    } catch (error) {
        console.log(error)
    }
}

useEffect(() => {
    getUser()
}, [])

useEffect(() => {
    if (user) {
        getGenderedUsers()
    }
}, [user])

const updateMatches = async (matchedUserId) => {
    try {
        await axios.put('https://[app].herokuapp.com/addmatch', {
            userId,
            matchedUserId
        })
        getUser()
    } catch (error) {
        console.log(error)
    }
}

const swiped = (direction, swipedUserId) => {
    console.log(direction, swipedUserId)
    if (direction === 'right') {
        updateMatches(swipedUserId)
    }

    setLastDirection(direction)
}

const outOfFrame = (name) => {
    console.log(name   ' left the screen!')
}

const matchedUserIds = user?.matches.map(({user_id}) => user_id).concat(userId)

const filteredGenderedUsers = genderedUsers?.filter(
    genderedUser => !matchedUserIds.includes(genderedUser.user_id)
)

if (user && genderedUsers) return (<>
    {user && <div className="dashboard">
        <ChatContainer user={user}/>
        <div className="swipe-container">
            <div className="card-container">

                {filteredGenderedUsers?.map((genderedUser) =>
                    <TinderCard
                        className='swipe'
                        key={genderedUser.user_id}
                        onSwipe={(dir) => swiped(dir, genderedUser.user_id)}
                        onCardLeftScreen={() => outOfFrame(genderedUser.first_name)}>
                        <div style={{backgroundImage: 'url('   genderedUser.url   ')'}} className='card'>
                            <h3>{'Name: '   genderedUser.first_name} <br/> {'Sport: '   genderedUser.about}</h3>
                        </div>
                    </TinderCard>)}
                <div className="swipe-info">
                    {lastDirection ? <p>You swiped {lastDirection}</p> : <p/>}
                </div>
            </div>
        </div>
    </div>}
</>)
    return <>Loading ...</>
} 
export default Dashboard

If it doesn't work, probably there's something wrong with the data. Provide more info about request.

  • Related