Axios request works only on second request

Issue

I am building a user login system and currently the way I have it set up, the user types in their email and password and then that information is sent to my server to be checked if accurate. If it is, I create a JWT token and send it back along with the mongodb _id of the user, I then in another request use the mongodb _id of the user from the first request to retrieve all the information (blogs in this case) associated with their mognodb _id. All of this works except for the issue I am running into is the axios request does not work the first time. So I have to hit submit to log in for an error to occur TypeError: Cannot read properties of undefined (reading 'data') and then when I hit submit again it works. I believe the problem is getBasic not getting set before the rest of the code runs on the first attempt, and on the second attempt it is already defined. I am not sure how I can fix this without maybe a manual timer before I do the getBasic.data.map, but I know there must be a better way to fix this.

And just to add this is just me trying to test this concept so I understand later things like bcrypt will need to be added to the password instead of plaintext in the database, etc.

Frontend:

 function Login() {
    
    const [user, setUser] = useState()
    const [getBasic, setGetBasic] = useState()

    const [Item, setItem] = useState({
        email: '',
        password: ''
    });

    async function fetchData() {
        const sentItem = {
            user: user.data.user._id,
        }

        await axios.post('http://localhost:3001/api', sentItem)
            .then(result => setGetBasic(result))
    }



    const loginFunc = async () => {
        const checkUser = {
            email: Item.email,
            password: Item.password
        }

        await axios.post('http://localhost:3001/api/login', checkUser)
            .then(result => setUser(result))
            .then(fetchData())  
    }







    if (user && getBasic) {

        localStorage.setItem('key', JSON.stringify(user.data.token))
        console.log(localStorage.getItem('key'))

        return (
            <div>
                <div>
                    <input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
                    <input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
                    <button onClick={loginFunc}>Submit</button>

                </div>


                {
                    getBasic.data.map(({ text, _id, }, i) => {
                        return (
                            <div>
                                <p>{_id}</p>
                                <p>{text}</p>
                            </div>
                        );
                    })
                }   
        )
    }
    return (

        <div>

            <input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
            <input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
            <button onClick={loginFunc}>Submit</button>

        </div>
    )
}

export default Login

Server:

app.post('/api/login', async (req, res) => {


    const user1 = await User.findOne({ email: req.body.email, password: req.body.password })

    if (user1) {
        const payload = { name: req.body.name, email: req.body.email };
        const token = jwt.sign(payload, private_key, { expiresIn: '200s' });
        console.log('correct login')
        res.status(200).send({ auth: true, token: true, user: user1 })

    } else {
        console.log('incorrect login')
    }

})


app.post('/api', async (req, res) => {

    Blog.find({ user: req.body.user }, function (err, result) {
        if (err) {
            console.log(err)
        } else {
            res.send(result)
        }
    })

})

Solution

While nature of React.useState is asynchronous, and changes will not be reflected immediately, closures take their role in situations like this, as pointed out in link in below.

More details provided here – The useState set method is not reflecting a change immediately

As stated in error from your example, cannot read data of undefined – meaning user is undefined, and you try to access to user.data which leads to error.

const sentItem = {
    user: user.data.user._id,
}

Instead of

await axios.post('http://localhost:3001/api/login', checkUser)
            .then(result => setUser(result))
            .then(fetchData())  

why don’t you try this (remove fetchData function if not reused on other places…):

const result = await axios.post('http://localhost:3001/api/login', checkUser);
const sentItem = {
   user: result.data.user._id,
}
axios.post('http://localhost:3001/api', sentItem)
        .then(result => setGetBasic(result));

setUser(result);

Answered By – Stefan Zivkovic

Answer Checked By – Jay B. (AngularFixing Admin)

Leave a Reply

Your email address will not be published.