React authentication flickering issue in changing route

Multi tool use
React authentication flickering issue in changing route
I'm building several application in react
, the last one is a admin panel so my react
app must have authentication, absolutely all of my products used Server Side Rendering
.
react
react
Server Side Rendering
For The last case, back-end team use pass me Token
for handling authorization, they technologies are Java
, Spring
, Redis
and etc base.
Token
Java
Spring
Redis
When I send username
and password
to their server, If the information be correct, they send for me token
and something else, I store it in localStorage
and change the root state
of react
application as login, because of this change app use a different routing
.
username
password
token
localStorage
state
react
routing
Everything looks good but what happen when I refresh the page? here the issue will happen, the login
state
is false
so app goes to login page, in componentDidMount
I read the localStorage
and find out Oah! I got the token before and the app is logged in, so change the login
state
to true
.
login
state
false
componentDidMount
localStorage
login
state
true
HERE, flickering happen, and I don't know how I can prevent this bad event.
My root state
is like below:
state
class App extends Component {
constructor() {
super();
this.state = { //the root state
login: false,
};
};
...
The componentDidMount
is like below:
componentDidMount
componentDidMount() {
this.loginStateHandler(!!JSON.parse(localStorage.getItem('accessToken')));
};
loginStateHandler = (login) => {
if (!login) {
localStorage.clear();
}
this.setState({
login,
});
};
And the switching between login route and app route is in render
function of root component that name is App
, see below code:
render
App
{
this.state.login
? (
<div className={styles['app-wrapper']}>
<div className={styles['app-wrapper__top']}>
<Button classes={{root: this.props.classes.exitButton}} onClick={this.logoutHandler}>
exit
</Button>
</div>
<AppRoutes key='appRoutes' fetchManager={this.fetchManager}/>
</div>
)
: (
<LoginRoutes loginHandler={this.loginHandler}/>
)
}
How I can write authentication that prevent this flickering. I want the app decide that the user is logged in or not before first render. is it possible?
3 Answers
3
You could put all that logic right in the constructor
instead, so that your login
variable in your state will be correct straight away:
constructor
login
class App extends Component {
constructor() {
super();
const login = !!JSON.parse(localStorage.getItem('accessToken');
if (!login) {
localStorage.clear();
}
this.state = { login };
}
}
constructor
@AmerllicA Yes, the
constructor
is run on the server if you are using renderToString
on the server.– Tholle
Jul 1 at 14:47
constructor
renderToString
So on the server how code can read
localStorage
?, it's for browser!– AmerllicA
Jul 1 at 16:00
localStorage
@AmerllicA You need to change the logic for the server of course, but to avoid flickering the
state
needs to be set in the constructor
.– Tholle
Jul 1 at 16:02
state
constructor
@AmerllicA If you want to server render and have it change depending on if the user is logged in or not, you have to talk with your backend developers how they know the user is logged in or not. If you can't, you could just render
null
on the server and just do the rendering in the browser when you know the user is logged in or not from localStorage, to circumvent the flickering.– Tholle
Jul 1 at 18:00
null
common pattern is to delay rendering until you read user login state from LocalStorage
for example in Redux has Redux persist library that stores redux state so it's kept on page refresh
recently they added HOC for waiting for loading the local state
https://github.com/rt2zz/redux-persist/blob/master/docs/PersistGate.md
you can see that this HOC has option for loading component so it displays that until local state is loaded
I suggest you do the same with some logic for waiting / showing loading until local storage is loaded
I just found a real good way by myself, at first I render
null
in render function of login
component, then put the below code in componentDidMount
life cycle method:
render
null
login
componentDidMount
setTimeout(() => {
if (!JSON.parse(localStorage.getItem('loginDetails'))) {
this.setState({
renderDOM: true,
})
}
}, 0);
This means, if the app is not login render the login
component, because of using setTimeout
, the condition disappear from call stack
and then after wiping stack
the condition goes to stack
so for this awesome delay when the app is logged in, you never see login page flickering.
login
setTimeout
call stack
stack
stack
But this way has something, maybe I guess it can be a SEO
issue, when somebody is logged-in and directly call some page like website.com/users-list-page/
then at first app goes to login
component and the login component
render null then app read the
localStorageand find out oOH!, I'm logged-in so
setStateto
logintrue
, then app goes to
users-list-pagewith related component, but _remember_, the
loginpage is rendered in server as
nulland because of the app was logged-in, the call back function of
setTimeoutdoesn't run anything, so when the
users-list-pageappear pressing <kbd>Ctrl</kbd>+<kbd>U</kbd> to show page
HTML` sources, you see nothing in
SEO
website.com/users-list-page/
login
component
null then app read the
and find out oOH!, I'm logged-in so
to
, then app goes to
with related component, but _remember_, the
page is rendered in server as
and because of the app was logged-in, the call back function of
doesn't run anything, so when the
appear pressing <kbd>Ctrl</kbd>+<kbd>U</kbd> to show page
<div id="root"></div>
This maybe cause to SEO
issues. I don't know, maybe someone has an idea?
SEO
Maybe there is an other way to avoid flickering and then render the users-list-page
DOM
from server. I've no idea yet. ☹️
users-list-page
DOM
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Does
constructor
run in server side?– AmerllicA
Jul 1 at 14:43