Home > Mobile >  React Context and Provider data doesn't get passed down as expected
React Context and Provider data doesn't get passed down as expected

Time:12-07

I am trying to update an object that another component is using.

My Context:

import React, {Component, createContext} from "react";
import jwt from 'jsonwebtoken';

const LOCAL_STORAGE_AUTH_TOKEN = 'authToken';

interface AuthenticatedUser {
    username?: string;
    guildName?: string;
    guildId?: string;
}

interface AuthContextType {
    authenticated: boolean; // to check if authenticated or not
    user: AuthenticatedUser; // store user details
    token: string; //jwt token
    refreshToken: string; //jwt refresh token
    handleAuthentication: (username: string, password: string) => Promise<void>; // handle login process
    logout: () => Promise<void>; // log out the user
}

export const AuthContext = createContext<AuthContextType>({
    authenticated: false, // to check if authenticated or not
    user: {}, // store all the user details
    token: '', // store all the user details
    refreshToken: '', //jwt refresh token
    handleAuthentication: (username: string, password: string): Promise<void> =>
        Promise.resolve(), // handle login process
    logout: (): Promise<void> => Promise.resolve(), // logout the user
});
AuthContext.displayName = 'AuthContext';

export class AuthProvider extends Component {
    state = {
        authenticated:false,
        user: {},
        token:'',
        refreshToken:''
    };
    
    constructor(props: any) {
        super(props);
        
        const token = window.localStorage.getItem(LOCAL_STORAGE_AUTH_TOKEN) || '';
        const jwtData = jwt.decode(token);
        
        let user = {};
        let authenticated = false;
        let refreshToken = '';
        if(jwtData && typeof jwtData !== 'string'){
            authenticated = true;
            user = { 
                username: jwtData.data?.username || '',
                // TODO: Add the other sources too
            }
        }
        
        this.state = {
            authenticated,
            user,
            token,
            refreshToken
        }
    }
    
    handleAuthentication = async (username: string, password: string):Promise<void> => {
        fetch(process.env.REACT_APP_API_ENDPOINT    "users/login", {
            method:"POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({username:username, password:password})
        })
            .then(response => response.json())
            .then((data) => {
                /*
                        This data is coming back correctly
                */
                const user = {
                    username: data?.user.username,
                    guildName: data?.user.guild_information[0].guildName,
                    guildId: "123123"
                }
                
                this.setState({
                    authenticated: true,
                    user: user,
                    token: data?.token,
                    refreshToken: data?.refreshToken
                })
                
                window.localStorage.setItem(LOCAL_STORAGE_AUTH_TOKEN, data?.token)
                //TODO: Also save token in localstorage
            })
            .catch((err) => {
                console.log(err)
            })
    }
    
    logout = async ():Promise<void> => {
        //TODO: Log out the current user
    }
    
    render() {
        const authProviderValue = { 
            ...this.state,
            handleAuthentication: this.handleAuthentication,
            logout: this.logout
        }
        
        return (
            <AuthContext.Provider value={authProviderValue}>
                {this.props.children}
            </AuthContext.Provider>
        )
        
    }
}

And the App component where I use it:

import React, {useContext} from "react";
import { useStyles} from "./style";
import {AuthContext, AuthProvider} from "../../context/UserContext";
import RoutesProvider from "./Routes";
import Login from "../Login";

export default function App() {
  const classes = useStyles();
  const { user } = useContext(AuthContext)
  
  return (
    <AuthProvider>
        {typeof user.username !== "undefined" ? (
          <div className={classes.content}>
            <RoutesProvider />
          </div>
        ):(
          <Login />
        )}
    </AuthProvider>
  )
}

Also wrapped the App component within the AuthProvider as recommended:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import { SnackbarProvider } from 'notistack';
import { AuthProvider} from "./context/UserContext";
import './styles/index.css'

ReactDOM.render(
  <React.StrictMode>
    <AuthProvider>
      <SnackbarProvider maxSnack={3}>
        <App />
      </SnackbarProvider>
    </AuthProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

It does take the initial value if I change it in the AuthContext. But after updating it does not update in the App component. I'm a little confused as to why this doesn't work as expected. If anyone knows what causes this (or if I'm doing it wrong) let me know please. Currently 5 hours into debugging...

CodePudding user response:

If I understand your question correctly, you're asking:

"Why your user constant doesn't update when your handleAuthentication method is successful"

The answer is because you're initializing your user constant outside of your provider.

Use your AuthProvider in your index.tsx instead of your App component like this:

<AuthProvider>
   <App />
</AuthProvider>

Or transfer your context logic in a children component

  • Related