I have a client implemented in react typescript, which needs to work with user data. Therefore, I've created an AppContext.
//appState.ts
export interface UserStateProperties {
authenticated: boolean,
user: GetUserResponse | undefined,
notificationManager: NotificationManager | undefined
}
export interface AppContextProperties {
userState: UserStateProperties,
setUserState: Dispatch<SetStateAction<UserStateProperties>>
}
const AppContext = React.createContext<AppContextProperties>({
userState: {
authenticated: false,
user: undefined, // userData like name, level, ...
notificationManager: undefined // contains socket to receive notifications
},
setUserState: () => {}
});
export default AppContext;
In my App component, I instantiate a state for the user and passed it as value to an AppContext.Provider.
// App.tsx
function App() {
const [userState, setUserState] = useState<UserStateProperties>({
authenticated: false,
user: undefined,
notificationManager: undefined
});
return (
<Suspense fallback={'Die Seite lädt...'}>
<AppContext.Provider value ={{userState, setUserState}}>
<Router history={createBrowserHistory()}>
<Switch>
<Route path='/' exact component={ Home }/>
<Route path='/auth/forgot' exact component = { Forgot } />
<Route path='/auth/:type' exact component={ Auth }/>
// A lot more components
<Route component={ ErrorPage }/>
</Switch>
</Router>
</AppContext.Provider>
</Suspense>
);
}
Each of my components (e.g Home)
// Home.tsx
...
return(
<Frame current='/'>
<section className='home-landingPage'>
...
</Frame>
)
are wrapped in a Frame component
// Frame.tsx
interface FrameProperties {
children: React.ReactNode | React.ReactNode[],
current?: string
}
export default function Frame(props: FrameProperties) {
return (
<div className='frame-container'>
<NavigationBar current={ props.current } />
{ props.children }
<Footer/>
</div>
)
}
which adds a NavigationBar to the component. In this NavigationBar, I am rendering things like signin/signup button (in case authenticated == false) or signout button, profile picture, level progress (in case authenticated == true). To ensure that the navigation bar displays the correct information, I use an effect hook, which updates the userStatus.
//Navigation.tsx
import AppContext from '../../../context/appState';
...
export default function NavigationBar(props: NavigationBarProps) {
const {userState, setUserState} = useContext(AppContext)
const updateUser = async () => {
fetchGetOwnUser().then(response => {
if(response.status === 200) {
setUserState({...userState, user: response.data}); // update user
}
}).catch(err => {
console.error(err);
});
console.log("USERSTATE AFTTER: ");
console.log(userState);
}
const updateAuthenticationStatus = async () => {
const accessToken = localStorage.getItem('accessToken');
if(accessToken) {
fetchVerifyToken({token: accessToken})
.then(response => {
if(response.status == 200){
const userId = getTokenPayload(accessToken).sub;
setUserState({authenticated: true, user: userState.user, notificationManager: userState.notificationManager || new NotificationManager(userId)}); //update authentication status of user
}
})
.catch(err => {
console.error(err);
});
console.log("USERSTATE AFTER: ");
console.log(userState);
}
useEffect(() => {
console.log("USERSTATE BEFORE: ");
console.log(userState);
if(userState.authenticated){
updateUser();
}else{
updateAuthenticationStatus();
}
}, []);
}
However, although updateAuthenticationStatus and updateUser are executed succesfully, the userState object does not change. The console shows the following output.
USERSTATE BEFORE: Object { authenticated: false, user: undefined, notificationManager: undefined }
USERSTATE AFTTER: Object { authenticated: false, user: undefined, notificationManager: undefined }
Thanks for any help in advance!