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
So your
useAuth
hook extractslogin
and other properties from whatever you pass in as thearg
parameter, falling back to an empty object.When you call
how exactly do you expect
{}
to contain alogin
function?I think what you actually wanted was for
useAuth
to be a simple wrapper arounduseContext(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
will return
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.