I am building a portfolio website and would like to implement a loading screen before each and every page get loaded or rendered in the browser. In my portfolio I have implemented a theme switcher( between light/dark) using react context api. I have tried to integrate a loading screen using this stack overflow thread as reference. But I couldn't find a way around from these solution to incorporate the current theme in to the loading component. I have tried building a custom loading screen and incorporate the theme in to it but for it requires the entire DOM of the home screen to be loaded and also tried using the useEffect hook, so that solution didn't work. Please Help me solve the issue or suggest a way around. Thank you!
Currently I have implemented it using a simple query selector to update the class
of a div
in index.html
file(within the React app's public folder) dynamically. The code is provided bellow.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.svgLoader {
animation: spin 4s linear infinite;
margin: auto;
}
.divLoader {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.dark {
background-color: #0d1117;
}
.light {
background-color: white;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!--loading screen-->
<div id="loader" class="divLoader">
<svg class="svgLoader" viewBox="0 0 100 100" width="10em" height="10em">
<path ng-attr-d="{{config.pathCmd}}" ng-attr-fill="{{config.color}}" stroke="none"
d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50" fill="#515390" transform="rotate(179.719 50 51)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 51;360 50 51"
keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</path>
</svg>
</div>
</div>
</body>
<script>
function setLoadTheme() {
const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const theme = darkMediaQuery.matches ? 'dark' : 'light';
document.getElementById('loader').className = `divLoader ${theme}`;
}
setLoadTheme();
</script>
</html>
I have take the code for the loader animation for here. actually this method worked with the help of custom script for getting the current theme from the browser defaults. But the issue comes when the actual user is toggling between the themes when they are interacting with the website. When the user toggle it manually I am not able to incorporate that change in to the loading screen's theme, since it is hard coded in the index.html
file. I am also sharing the context that I have created for toggling between the themes in my web app.
Theme.js
import { createContext, useEffect, useState } from 'react'
import PropTypes from 'prop-types'
const ThemeContext = createContext()
function ThemeProvider({ children }) {
const [themeName, setThemeName] = useState('light')
useEffect(() => {
const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
setThemeName(darkMediaQuery.matches ? 'dark' : 'light')
darkMediaQuery.addEventListener('change', (e) => {
setThemeName(e.matches ? 'dark' : 'light')
})
}, [])
const toggleTheme = () => {
const name = themeName === 'dark' ? 'light' : 'dark'
localStorage.setItem('themeName', name)
setThemeName(name)
}
return (
// eslint-disable-next-line react/jsx-no-constructed-context-values
<ThemeContext.Provider value={[{ themeName, toggleTheme }]}>
{children}
</ThemeContext.Provider>
)
}
ThemeProvider.propTypes = {
children: PropTypes.node.isRequired,
}
export { ThemeProvider, ThemeContext }