When a user navigate to my site, I need to initialize the React web app as follow:
If the incoming user has never requested the web app, I want to set the UI language to browser default (navigator.language).
If the incoming user has already visited the site and chosen a prefered language (lang stored in the localStorage), I want to init the UI with this language.
If the incoming user has an account and is already connected (token available in localStorage), I want to auto-connect him and render the app accordingly : login button transformed into a welcome message, UI language set to user preference.
To do so, I'm using React Context API and a defaultUser
object.
defaultUser
: init a default user
const defaultUser = {
language: 'en_EN',
isConnected: false
}
Context
: create a default context
export const AppContext = createContext({
connectedUser: defaultUser,
})
Provider
: create the provider with default context
export function AppProvider({ children }: any) {
[...]
const provider = {
connectedUser
}
return (
<AppContext.Provider value={provider}>
{children}
</AppContext.Provider>
)
}
App
: init the provider during app start up
export class App extends Component {
static contextType = AppContext
render() {
return (
<AppProvider>
<AppContainer />
</AppProvider>
)
}
}
AppContainer
: render the app
export class AppContainer extends Component {
static contextType = AppContext
componentDidMount() {
/** If user is not connected, verify if a stored session exists and use it to connect user */
if (!this.context.connectedUser.isConnected) {
[...do things...]
}
}
The whole mecanism works well except an annoying thing : the web app is systematically initialized with default user values, until the AppContainer::componentDidMount()
do the real init job.
This is causing a sort of flickering effect.
I'm struggeling for 2 days on how to fix that, trying to perform Context
init before <AppContainer />
rendering, and I'm stuck.
Any recommandations?
EDIT : For clarity, I'm adding a diagram. Currently :
start
.start
with default value.end
is reached.end
.Any layout change during these two steps (UI language, UI modification based on user permissions) are clearly visible to the user and generate a sort of flickering.
I found sort of a solution by simply conditionning <AppContainer/>
loading, postponing it to the end of the sequence. However instead of having flickering I have now a lag and other unwanted side effects.
The goal would be to differ all the sequence before React Act is rendered, and after Window
is available. Then dynamically create the Context, then render all.
I think the point would be resolved if I could dynamically create the AppContext and pass a variable to createContext()
during App constructor()
or maybe componentWillMount()
(not sure when Window is available), but then TypeScript get into play with types issues and I'm still stuck.
You didn't share the code that initializes the context, but I suspect you put the default value to be either a hardcoded value, or navigator.language
and therefore experience the flickering. I'd like to suggest two ways to solve this:
Solution 1
Perhaps instead of having a hardcoded default context you could generate the default context programmatically by accessing localStorage.get('lang')
or similar? There is a slight drawback to this solution though: You will be mixing concerns of react and the browser, but I think in this case it's an alternative to consider, because it's very simple and obvious to the reader.
Solution 2
Alternatively, when calling ReactDOM.render
you could pass down whatever you need from localStorage
as a prop to your application and so you keep the browser related logic separate from the pure React stuff.
I hope this makes sense.