user10903468
user10903468

Reputation:

why useContext updated only in children components/functions?

Hi guys so I'm working on a React App and I'm using React-Hooks . I created a new context with by using creatContext that holds the userId and token . I set the navbar to change when user is logged , and I also set the Redirect from the AuthPage to another page when user is logged. The problem is that when I do login , the navbar actually changes but it does no redirect me to where it should be , even though I logged in . Why isn't it updating the AuthPage? I also tried to use

The file where I create the context :


import React ,{  createContext} from 'react' ;


 const context= createContext({
    userId:null,
    token: null ,
    login: (userId, token , tokenExpiration)=> {} , 
    logout: ()=>{} 
});

export default context ;

App.js:


import './App.css';
import AuthPage from './pages/AuthPage'
import {BrowserRouter, Route , Redirect , Switch} from 'react-router-dom'
import EventsPage from './pages/EventsPage';
import BookingsPage from './pages/BookingsPage';
import MainNavigation from './components/Navigation/MainNavigation'
import AuthContext from './context/auth-context';
import { useState , useContext, useMemo } from 'react';





function App() {
  const context = useContext(AuthContext);
  const[token , setToken] =useState(null) ;
  const[userId , setUserId] =useState(null);


  const login=(token,userId , tokenExpiration)=>{
    setToken(token) ;
    setUserId(userId);
  }
  const logout=()=>{
    setToken(null);
    setUserId(null);
  }

  const updateAndRender =useMemo(() => ( {
    token:token , 
    userId:userId ,
    login:login,
    logout:logout 
  }
    
  ), [token , userId , login]);
  








  return (
    <BrowserRouter>
    <AuthContext.Provider 
    value={updateAndRender}
    >
    <MainNavigation/>
    <main className="main-content">
    <Switch>
    {context.token && <Redirect from="/auth" to="/bookings" exact /> }

      {context.token && <Redirect from="/auth" to="/events" exact /> }
    {!context.token && <Redirect from="/" to="/auth" exact />}
    {!context.token && <Redirect from="/bookings" to="/auth" exact />}
    
   {!context.token && <Route path="/auth" component={AuthPage} />}
   <Route path="/events" component={EventsPage} />
   { <Route path="/bookings" component={BookingsPage} />}



      </Switch>
      </main>
      </AuthContext.Provider>

    </BrowserRouter>
  );
}

export default App;

AuthPage:

import React , {useContext, useState , useEffect} from 'react';
import './AuthPage.css';

import AuthContext from '../context/auth-context';





export default function Authpage(props) { 
const [email , setEmail]=useState(" ");
const [password , setPassword]=useState(" ");
const [isLogin , setIsLogin]=useState(true);
const context = useContext(AuthContext);




const swithmodeHandler=()=> { setIsLogin(!isLogin)}


    const submitHandler=async (e)=>{
       
        e.preventDefault();
      let requestBody  ={
            query:
  `
            query{
                login(email:"${email}" , password:"${password}")
                {
                    userId
                    token
                    tokenExpiration
                }
            }
            `
            

        }

        if(isLogin){
            requestBody={
                query:` 
                
                
                mutation{
                    createUser(userInput:{email:"${email}" , password:"${password}" })
                    {
                        _id
                        email
                    }
                }`
            }
        }
        
try {
    
   const requestToGql = await fetch('http://localhost:3001/graphql' , {
        method:'POST' , 
        body:JSON.stringify(requestBody) ,
        headers:{
            'Content-Type': 'application/json'

        }})

        if(requestToGql.status!==200 && requestToGql.status!==201){
            throw new Error("Failed creating user");
        }
        const resData=await requestToGql.json() ;
       

       if((Object.keys(resData.data.login) !==0) || resData.data.login.token){
           context.login(resData.data.login.userId,
            resData.data.login.token, 
            resData.data.login.tokenExpiration
            ) ;
       }
return resData ;

} catch (error) {
    console.log(error)
}
   


       



    }
    
return <form className="auth-form" onSubmit={submitHandler}>
    
    <div className="form-control">  <label htmlFor="email"> Email</label>
        <input value={email}  type="email" id="email"  onChange={e=>setEmail(e.target.value)}/>
    </div>
    <div className="form-control">
        <label htmlFor="password"> Password</label>
        <input value={password} type="password" id="password"  onChange={e=>setPassword(e.target.value)}/>
    </div>
    <div className="form-actions">
    <button type="submit"> Submit</button>
    <button  onClick={swithmodeHandler} type="button"> {isLogin? "Signup" : "Login"}</button>
    


    </div>
</form>

}

the navbar component:

import React , {useContext} from 'react' ;
import {NavLink} from 'react-router-dom'
import './MainNavigation.css'
import AuthContext from '../../context/auth-context'

 

export default function MainNavigation(props){
    const context = useContext(AuthContext) ;
 
    

    return(
    <header className="main-navigation">

  
    <div className="main-navigation_logo"> 
        <h1> Yul's Gym</h1>
    </div>
    <nav className="main-navigation_items">
    <ul>
        { !context.token &&
             <li>
             <NavLink to="/auth"> Authenticate</NavLink>
         </li>
        }
       
        <li>
            <NavLink to="/events"> Events</NavLink>
        </li>
        { context.token &&
        
            <li>
            <NavLink to="/bookings"> Bookings</NavLink>
        </li>
        }
        
    </ul>
    </nav>
    </header>
    )
}

Upvotes: 0

Views: 185

Answers (1)

Khaled Ahmed
Khaled Ahmed

Reputation: 1134

you can't use any context inside the component that you initialize the context in

for your case here you should create another component for example router and use the context inside and initialize the context in the App component

Layout.js

import AuthPage from './pages/AuthPage'
import {BrowserRouter, Route , Redirect , Switch} from 'react-router-dom'
import EventsPage from './pages/EventsPage';
import BookingsPage from './pages/BookingsPage';
import MainNavigation from './components/Navigation/MainNavigation'
import AuthContext from './context/auth-context';
import { useState , useContext, useMemo } from 'react';





function Layout() {
  const context = useContext(AuthContext);
  const[token , setToken] =useState(null) ;
  const[userId , setUserId] =useState(null);


  const login=(token,userId , tokenExpiration)=>{
    setToken(token) ;
    setUserId(userId);
  }
  const logout=()=>{
    setToken(null);
    setUserId(null);
  }

  const updateAndRender =useMemo(() => ( {
    token:token , 
    userId:userId ,
    login:login,
    logout:logout 
  }
    
  ), [token , userId , login]);
  








  return (
    <BrowserRouter>
    <MainNavigation/>
    <main className="main-content">
    <Switch>
    {context.token && <Redirect from="/auth" to="/bookings" exact /> }

      {context.token && <Redirect from="/auth" to="/events" exact /> }
    {!context.token && <Redirect from="/" to="/auth" exact />}
    {!context.token && <Redirect from="/bookings" to="/auth" exact />}
    
   {!context.token && <Route path="/auth" component={AuthPage} />}
   <Route path="/events" component={EventsPage} />
   { <Route path="/bookings" component={BookingsPage} />}



      </Switch>
      </main>
      

    </BrowserRouter>
  );
}

export default Layout;

App.js

import './App.css';
import Layout from '../Layout/Layout.js'
import AuthContext from './context/auth-context';

function App() {
  return (
      <AuthContext.Provider 
    value={updateAndRender}
    >
    <Layout />
    </AuthContext.Provider>
    );
}

export default App;

Upvotes: 2

Related Questions