Search code examples
reactjspromiselocal-storageunhandled-promise-rejection

localStorage functions not working, getting a set headers error


localStorage.setItem('auth-token', '');

This line is where the suspected error is happening. I have read other questions that receive this error that I am going to post below. I have seen it commonly where there needs to be an async or an await somewhere, and I am missing it.

Here is the full error.

(node:2211) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:547:11)
    at ServerResponse.header (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/response.js:267:15)
    at checkToken (/Users/devintripp/Desktop/unt-library-system/server/controllers/user-ctrl.js:132:32)
    at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/route.js:137:13)
    at Route.dispatch (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/route.js:112:3)
    at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
    at /Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:281:22
    at Function.process_params (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:335:12)
    at next (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:275:10)
    at Function.handle (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:174:3)
    at router (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:47:12)
    at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:317:13)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2211) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:2211) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I have boiled it down to this code.

import React, { useState, useEffect } from 'react';
import { BrowserRouter , Route, Switch } from 'react-router-dom';
import Axios from 'axios';

import { NavBar } from './components';
import SignIn  from './components/auth/SignIn';
import Register from './components/auth/Register';
import { BooksList, BooksInsert, BooksUpdate } from './pages';
import  UserContext  from './context/UserContext'

import 'bootstrap/dist/css/bootstrap.min.css'

function App() {

    //global state for the app to use
    const [userData, setUserData] = useState({
        token: undefined,
        user: undefined
    })


    useEffect( () => {
        const checkLoggedIn = async () => {
            let token = localStorage.getItem('auth-token');
            
            console.log(token); // this prints "" to the console in the browser
            localStorage.setItem('auth-token', ''); //this code never changes the headers in chrome

            if(token === null || token === ""){
                localStorage.setItem('auth-token', '');
                token = "";
            }

            const tokenResponse = await Axios.post("http://localhost:8174/user/checkToken", null,  {headers: {"x-auth-token": token}});
            if(tokenResponse.data){
                const userRes = await Axios.get("http://localhost:8174/user/", {headers: {"x-auth-token": token}})
                setUserData({token, user: userRes.data})
            }

        }



        checkLoggedIn();
    }, []);

return (
<div></div>
);


}

also a thing to note is that the localStorage.setItem() is not setting any headers to "". It is not setting headers at all.

I have read this question: Error: Can't set headers after they are sent to the client

I am not in a body or finished state, nor am I calling res.writeHead() anywhere. I could post my backend calls to /checkToken and / if that would help.

here is my checkToken and get / calls

checkToken = async (req, res) => {
    try {
        const token = req.header("x-auth-token")
        if(!token){
            res.json(false);
        }


        const verified = jwt.verify(token, process.env.JWT_SECRET);
        if(!verified){
            return res.json(false);
        }


        const user = await User.findById(verified.id);
        if(!user){
            return res.json(false)
        }

        return res.json(true)
    } catch (err) {
        return res.status(500).json({msg: err.message});
    }
}

getUser = async (req, res) => {
    const user = await User.findById(req.user);
    // console.log(user)
    if(!user){
        console.log("no user found")
        res.json({msg: "user not found"})
    }
    res.json({
        name: user.name,
        id: user._id
    })
}

and here is where those are being called.

const express = require("express");
// const userCtrl = require("../controllers/user-ctrl");
const router = express.Router();
const UserCtrl = require("../controllers/user-ctrl");
const auth = require("../middleware/auth");





router.post('/register', UserCtrl.registerUser);
router.post('/login', UserCtrl.loginUser);
router.delete('/delete', auth, UserCtrl.deleteUser);
router.post('/checkToken', UserCtrl.checkToken);
router.get('/', auth, UserCtrl.getUser);


module.exports = router;

and lastly here is my middleware

const jwt = require('jsonwebtoken');


const auth = (req, res, next) => {

    try{
        const token = req.header("x-auth-token")
        if(!token){
            return res.status(401).json({msg: "No authentication token found, auth denied."})
        }

        
        const verified = jwt.verify(token, process.env.JWT_SECRET);
     
        if(!verified){
            return res.status(401).json({msg: "Token verification failed, auth denied."})
        }

        req.user = verified.id
        // res.end();
        next();
    } catch (err) {
        res.status(500).json({error: err.message});
    }

}


module.exports = auth;

The answer below answered the error. I wasn't returning the responses that I was sending out so it was sending multiple responses and falling through like not putting a break; at the end of a case.

localStorage however is still an issue and is unable to be set in chrome.


Solution

  • It is the same issue mentioned in comments & close request.

    The Problem

    You try to send multiple responses to the same request. Trying to send multiple responses to the same request is what causes the Express error.

    This happens in your getUser code as well as in your checkToken code.

    • getUser attempts to send multiple responses if the condition !user is true (if user evaluates to false).
    • checkToken will attempt to send multiple responses if one of !token, !verified or !user is true.