signinSilent triggering rerenders
I might be doing something incorrectly here.
I am trying to do activity based token refresh. I made a basic app to test the issue out to find out if it might happen to be me.
My use case: We do not have auto renew. We kick a user off after 30 minutes of inactivity. If active and within a certain timeframe it will trigger a silent renew using signinSilent. This will reset the activity. The problem is it is causing the app and all it's children to rerender. My app in the grand scheme is a module federation app so I don't have the ability to have a top level store as this each module is meant to be independently maintained. This is causing a major issue and would love to fix it.
const BaseShell = () => {
return (
<AuthProvider {...IDENTITY_CONFIG}>
<Routes />
</AuthProvider>
)
}
Routes (just re-using a file name)
const Routes = () => {
const auth = useAuth();
switch (auth.activeNavigator) {
case "signinSilent":
return <div>Signing you in...</div>;
case "signoutRedirect":
return <div>Signing you out...</div>;
}
if (auth.isLoading) {
return <div>Loading...</div>;
}
if (auth.error) {
return <div>Oops... {auth.error.message}</div>;
}
if (auth.isAuthenticated) {
return (
<div>
Hello {auth.user?.profile.sub}{" "}
<button onClick={() => void auth.removeUser()}>Log out</button>
<Test refresh={() => {
console.log("silent called")
auth.signinSilent()
}} />
</div>
);
}
return <button onClick={() => void auth.signinRedirect()}>Log in</button>;
}
Test File:
export const Test = (props) => {
console.log("refresh silent")
const [testState, setTestState] = useState(1)
return <p><button onClick={() => {
setTestState(2)
props.refresh()
}}>Refresh Silent {testState}</button></p>
}
I confirmed the rerendering by the state resetting back to 1 after login has occurred. This should not be the expected behaviour. I appreciate any help.
Hard to say what the problem is, you need to track it down, if there is a problem within this library, i am happy to apply patches...
Same scenario, re-render whole application after signinSilent(). How could be prevent re-render after signinSilent()?
I think you can simulate by adding some log at top level component.
const BaseShell = () => {
React.useEffect(() => {
console.log("BaseShell just rendered at %o", new Date());
}, []);
return (
<AuthProvider {...IDENTITY_CONFIG}>
<Routes />
</AuthProvider>
)
}
I can confirm that placing a console.log like the OT did is causing results... but that should not really matter that is the way react continuously works. If it is causing problems, it's probably along the way you have react attributes that will have a new reference, and that is probably the real cause of your issues with the rerenders
Like
const MainAttribute = ()=> {
const SomeFunction = ()=>(<p>hi</p>)
return <p><SomeFunction/></p>
}
the solution for this can be
const MainAttribute = ()=> {
const SomeFunction = ()=>(<p>hi</p>)
return <p>{SomeFunction()}</p>
}
but that is not really the correct react way, but it will work and will not cause a true rerender (blinking screen)
const SomeFunction = ()=>(<p>hi</p>)
const MainAttribute = ()=> {
return <p><SomeFunction/></p>
}
SomeFunction is now having a constant reference, and with each call it will not cause any so called rerenders.
~~So I think the issue is minor and should not be fixed~~
Honestly this is the correct way this library works beacuse what if the token cannot be renewed. auth.isAuthenticated should be false, and your code should be able to react on it.
@dstone1983 were you able to fix this? I am also facing the same issue, loosing all the data entered. Any help is much appreciated.
Here's the <Routes> that OP posted, the problem is in the sequence of if()/switch-statements:
const Routes = () => {
const auth = useAuth();
switch (auth.activeNavigator) {
case "signinSilent":
return <div>Signing you in...</div>;
case "signoutRedirect":
return <div>Signing you out...</div>;
}
if (auth.isLoading) {
return <div>Loading...</div>;
}
if (auth.error) {
return <div>Oops... {auth.error.message}</div>;
}
if (auth.isAuthenticated) {
return (
<div>
Hello {auth.user?.profile.sub}{" "}
<button onClick={() => void auth.removeUser()}>Log out</button>
<Test refresh={() => {
The issue is that when signinSilent is called (new tokens are retrieved), even though you're still authenticated (auth.isAuthenticated), auth.activeNavigator will contain the value signinSilent.
And because the case that returns Signing you in... is above the case that returns the app, the whole app gets removed from the DOM.
Moving the if(auth.isAuthenticated) case to the top will fix this.
a gotcha: if you do something like console.log('isAuthenticated: ', auth.isAuthenticated), you'll see false in the console sometimes, that's from the instance of the app being loaded into the iFrame during silent renewal.