skip to Main Content

I’m using an AuthContext on a node, express, mysql, react project and I want to keep my /dashboard away from strangers, so I added an auth check before that route.

The thing is, when I try to log in, a wild ‘TypeError: login is not a function’ appears.

This is my context

import React, { createContext, useState } from 'react';
import axios from 'axios';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [userAuthenticated, setUserAuthenticated] = useState(false);

  const API = process.env.REACT_APP_API_URL;

  const login = async (email, password) => {
    try {
      console.log('Intentando iniciar sesión con email:', email, password);
      const response = await axios.post(`${API}users/login/`, { email, password });
      if (response.data.isAuthenticated) {
        setUserAuthenticated(true);
        return true;
      } else {
        return false;
      }
    } catch (error) {
      console.error('Error en el inicio de sesión:', error);
      return false;
    }
  };

  const logout = () => {
    setUserAuthenticated(false);
  };

  return (
    <AuthContext.Provider value={{ userAuthenticated, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth(arg) {
  const { userAuthenticated, login, logout } = arg || {};

  return { userAuthenticated, login, logout };
}

My Login component

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from './authContext';
import { Container, Paper, TextField, Button, Typography } from '@mui/material';

const formStyle = {
  margin: '20px',
  padding: '20px',
};

function Login() {
  const { login } = useAuth(); 
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState(null);
  const navigate = useNavigate();

  const handleEmailChange = (e) => {
    setEmail(e.target.value);
  };

  const handlePasswordChange = (e) => {
    setPassword(e.target.value);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    console.log('handleSubmit iniciado');
    const isAuthenticated = await login(email);
    if (isAuthenticated) {
      console.log('Redirigiendo a /dashboard');
      navigate('/dashboard');
    } else {
      setError('Autenticación fallida');
    }
  };

  return (
    <Container component="main" maxWidth="xs">
      <br />
      <Paper elevation={3}>
        <Typography variant="h5" component="h1">
          <br />
          Debes autenticarte
        </Typography>
        <div style={formStyle}>
          <form onSubmit={handleSubmit}>
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              label="Correo Electrónico"
              name="email"
              autoComplete="email"
              autoFocus
              value={email}
              onChange={handleEmailChange}
            />
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              name="password"
              label="Contraseña"
              type="password"
              id="password"
              value={password}
              onChange={handlePasswordChange}
            />
            {error && <Typography variant="body2" color="error">{error}</Typography>}
            <br /><br />
            <Button
              type="submit"
              fullWidth
              variant="contained"
              color="warning"
            >
              Iniciar sesión
            </Button>
          </form>
        </div>
      </Paper>
      <br />
    </Container>
  );
}

export default Login;

And my App.js

import './App.css';
import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom';
import { useState, useEffect } from 'react';
import axios from 'axios';

import { AuthProvider, useAuth } from './components/authContext';
import { CartProvider } from './products/cartContext'; 

import CompShowProducts from './products/products';
import CompCreateProduct from './products/createProduct';
import CompEditProduct from './products/editProduct';

import Login from './components/login'

import Header from './components/header';
import Footer from './components/footer';

import Home from './components/home';
import SearchResultPage from './components/searchResultPage';
import ProductGrid from './products/productGrid';
import MayoristaProductGrid from './products/mayProductGrid';
import ShoppingCart from './products/cart';
import AboutUs from './components/aboutUs';
import CategoryProducts from './products/categoryProducts';
import LocalLocations from './components/branches';
import ContactButton from './components/contactButton';
import GoToShopButton from './components/goToShopButton';

function App() {
  const { userAuthenticated } = useAuth();
  const [products, setProducts] = useState([]);
  const [brands, setBrands] = useState([]);
  const [categories, setCategories] = useState([]);
  const [searchResults, setSearchResults] = useState([]);
  
  const API = process.env.REACT_APP_API_URL;
  const productsAPI = `${API}products/`;

  axios.defaults.withCredentials = true;

  useEffect(() => {
    const fetchProducts = async () => {
      try {
        const response = await axios.get(productsAPI);
        setProducts(response.data);

        const uniqueBrands = [...new Set(response.data.map((product) => product.brand))];
        setBrands(uniqueBrands);

        const uniqueCategories = [...new Set(response.data.map((product) => product.category.name))];
        setCategories(uniqueCategories);
      } catch (error) {
        console.error('Error al obtener los datos:', error);
      }
    };

    fetchProducts();
    // eslint-disable-next-line
  }, []);

  return (
    <AuthProvider>
    <CartProvider>
      <div className="App">
        <BrowserRouter>
          <Header setSearchResults={setSearchResults} />
          <Routes>
            <Route path='/' element={<Home />} />
            <Route path="/productos" element={
              <ProductGrid
                products={products}
                brands={brands}
                categories={categories}
              />
            } />
            <Route path="/mayorista" element={
              <MayoristaProductGrid
                products={products}
                brands={brands}
                categories={categories}
              />
            } />
            <Route path='/cart' element={<ShoppingCart />} />
            <Route path="/dashboard" element=
            {userAuthenticated ? <CompShowProducts /> : <Navigate to="/login" />} />
            <Route path="/create" element={<CompCreateProduct />} />
            <Route path="/edit/:id" element={<CompEditProduct />} />
            <Route path="/search-results" element={<SearchResultPage searchResults={searchResults} />} />
            <Route path="/aboutus" element={< AboutUs />} />
            <Route path="/productos/:category" element={<CategoryProducts />} />
            <Route path="/sucursales" element={<LocalLocations />} />
            <Route path="/login" element={<Login />} />
          </Routes>
          <GoToShopButton/>
          <ContactButton/>
        </BrowserRouter>
        <Footer />
      </div>
    </CartProvider>
    </AuthProvider>
  );
}

export default App;

I checked if the login function was out of scope or if any login const was declared before but I didn’t find anything. I also checked if it was a case issue, but I found nothing.

I’d really appreciate if any of you could help me out. I’m stuck in here and I have wasted the last two days trying to fix it without any clue. Also, I apologize if my english is not good enough, I’m argentinian.

Thankss

2

Answers


  1. export function useAuth(arg) {
      const { userAuthenticated, login, logout } = arg || {};
    
      return { userAuthenticated, login, logout };
    }
    

    So your useAuth hook extracts login and other properties from whatever you pass in as the arg parameter, falling back to an empty object.

    When you call

    const { login } = useAuth();
    

    how exactly do you expect {} to contain a login function?

    I think what you actually wanted was for useAuth to be a simple wrapper around useContext(AuthContext).

    You should also provide a sane default value for your context to avoid runtime errors and also to let you know when your component hierarchy is incorrect

    import { createContext, useContext, useState } from "react";
    import axios from "axios";
    
    const AuthContext = createContext({
      userAuthenticated: false,
      login: async () => {
        console.warn("AuthContext#login used without provider");
        return false;
      },
      logout: () => {
        console.warn("AuthContext#logout used without provider");
      },
    });
    
    // ...
    
    export const useAuth = () => useContext(AuthContext);
    
    Login or Signup to reply.
  2. export function useAuth(arg) {
      const { userAuthenticated, login, logout } = arg || {};
    
      return { userAuthenticated, login, logout };
    }
    

    will return

    {
    userAuthenticated: undefined,
    login: undefined,
    logout: undefined
    }
    

    when you create your hook without arguments i.e const {login} = useAuth() login will be undefined. Because it’s trying to extract the Key "login" from {}, which is undefined. In your return statement, login does not refer to the login function you defined previously in that file, it refers to the login variable you declared in your hook.

    The solution is doing it the other way around.

    export function useAuth(arg) {
      const [userAthenticated, setUserAuthenticated] = useState(false);
      
      const login = useCallback((email, password)=>{
        //your login function
      },[arg]);
    
      const logout = useCallback(()=>{
        //your logout function
      },[arg]);
     
      return {userAuthenticated, login, logout};
    }
    
    export function AuthProvider({ children }) {
      const { userAuthenticated, login, logout } = useAuth();
      return (
        <AuthContext.Provider value={{ userAuthenticated, login, logout }}>
          {children}
        </AuthContext.Provider>
      );
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search