I tried making a global state by using a Context and have the following for my AuthProvider.js file
import { createContext, useState } from "react"; const AuthContext = createContext({}); export const AuthProvider = ({ children }) => { const [auth, setAuth] = useState({}); return ( <AuthContext.Provider value={{ auth, setAuth }}> {children} </AuthContext.Provider> ) } export default AuthContext;
I want to be able to change its boolean state within an if-else instead of a button with an onCLick event in the tutorial but it shows an error in the console: setAuth is not a function
function Home() { const setAuth = useContext(AuthProvider); const [usernameReg, setUsernameReg] = useState(""); const [passwordReg, setPasswordReg] = useState(""); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [loginStatus, setloginStatus] = useState(""); const uLogin = document.getElementById('userLogin'); const pLogin = document.getElementById('passwordLogin'); const register = () => { if (usernameReg === "" || passwordReg === ""){ alert("Fields can't be blank"); return; } Axios.post('http://localhost:3001/register', { username: usernameReg, password: passwordReg, }).then((response) => { console.log(response); }); } const login = () => { Axios.post('http://localhost:3001/login', { username: username, password: password, }).then((response) => { if (response.data.message){ setloginStatus(response.data.message); alert(response.data.message); } else{ setloginStatus(response.data[0].username); setAuth(true); <====================================== here window.location.href = "/map"; } console.log(response.data); }); } return ( <div className="Home"> <h1>Register</h1> <label>Username: </label> <input type="text" onChange={(e) => { setUsernameReg(e.target.value) }}/> <label><p></p>Password: </label> <input type="text" onChange={(e)=> { setPasswordReg(e.target.value) }}/> <p></p> <button onClick={register}> Register </button> <h1>--------------------------------------------------------</h1> {/*<form id="loginForm">*/} <h1>Log In</h1> <input id="userLogin" type="text" placeholder="Username" onChange={(e) => { setUsername(e.target.value) }} /> <p></p> <input id="passwordLogin" type="password" placeholder="Password" onChange={(e) => { setPassword(e.target.value) }} /> <p></p> <button onClick={login}> Log In </button> {/*</form>*/} <h1>{loginStatus}</h1> </div> ) }
my App.js is below
import React, { useState } from "react"; import { BrowserRouter,Route, Routes} from 'react-router-dom'; import './App.css'; import Home from "./Pages/Home"; import MapPage from "./Pages/MapPage.js"; import ProtectedRoutes from "./ProtectedRoutes"; import { AuthProvider } from "./components/AuthProvider" function App() { const [auth, setAuth ] = useState(false); //const [auth, setAuth] = useState(false); return ( <BrowserRouter> <Routes element ={<AuthProvider />} value={{auth, setAuth}}> <Route element ={<ProtectedRoutes />}> <Route element={<MapPage />} path="/map" exact/> </Route> <Route path="/" element={<Home />}/> </Routes> </BrowserRouter> ); } export default App;
index.js below
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { AuthProvider } from './components/AuthProvider' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <AuthProvider> <App /> </AuthProvider> ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bitly/CRA-vitals reportWebVitals();
I’m very new to JavaScript and I’ve only been following along tutorials for the functions or features I need for the project. Is there a way for this?
I’m also guessing that the code for the context file is overkill for a simple Boolean state.
Advertisement
Answer
You probably want to check the context docs again, and try to understand how and why you set them up, and how to access them. You tried accessing the context by passing the provider as a param to the useContext hook. This is wrong, you need to pass the Context itself. You also called the return from the context setAuth, but the return is the context itself. I urge you to read the documentation thourougly. However, this is probably what you wanted:
type AuthContextType = { auth: boolean; setAuth: Dispatch<SetStateAction<boolean>>; }; const AuthContext = createContext<AuthContextType | undefined>(undefined); export const AuthProvider = ({ children }: PropsWithChildren<{}>) => { const [auth, setAuth] = useState(false); return ( <AuthContext.Provider value={{ auth, setAuth }}> {children} </AuthContext.Provider> ); }; const useAuthContext = () => { const ctx = useContext(AuthContext); if (!ctx) throw new Error("No AuthContext Provider found"); return ctx // as AuthContextType<...> if you need the context to be generic and to infer generics. }; function Home() { const { auth, setAuth } = useAuthContext(); ... }